/*
 * MegaMek - Copyright (C) 2000-2003 Ben Mazur (bmazur@sev.org)
 *
 *  This program is free software; you can redistribute it and/or modify it
 *  under the terms of the GNU General Public License as published by the Free
 *  Software Foundation; either version 2 of the License, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 *  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 *  for more details.
 */

package megamek.common;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import megamek.common.weapons.EnergyWeapon;
import megamek.common.weapons.GaussWeapon;

/**
 * This class creates an Aerospace entity.
 * 
 * @author Taharqa
 */
public class Aero extends Entity implements Serializable
{

	/** The constant serialVersionUID. */
	private static final long serialVersionUID = 7196307097459255187L;

	/** The mbImmobile. */
	private boolean mbImmobile = false;

	/** The mbiImmobile hit. */
	private boolean mbImmobileHit = false;

	/** The movement damage. */
	protected int movementDamage = 0;

	/** Location constant: Nose. */
	public static final int LOC_NOSE = 0;

	/** Location constant: left wing. */
	public static final int LOC_LWING = 1;

	/** Location constant: right wing. */
	public static final int LOC_RWING = 2;

	/** Location constant: Aft. */
	public static final int LOC_AFT = 3;

	/** Location constant: Wings. */
	public static final int LOC_WINGS = 4;

	/** Ramming angles constant for toward DIR. */
	public static final int RAM_TOWARD_DIR = 0;

	/** Ramming angles constant for toward OBL. */
	public static final int RAM_TOWARD_OBL = 1;

	/** Ramming angles constant for away from OBL. */
	public static final int RAM_AWAY_OBL = 2;

	/** Ramming angles constant for away from DIR. */
	public static final int RAM_AWAY_DIR = 3;

	/** Heat type constant: single heat. */
	public static final int HEAT_SINGLE = 0;

	/** Heat type constant: double heat. */
	public static final int HEAT_DOUBLE = 1;

	/** Cockpit type constant: standard. */
	public static final int COCKPIT_STANDARD = 0;

	/** Cockpit type constant: small cockpit. */
	public static final int COCKPIT_SMALL = 1;

	/** Cockpit type constant: command console. */
	public static final int COCKPIT_COMMAND_CONSOLE = 2;

	/** The constant specifying the cockpit types. */
	public static final String[] COCKPIT_STRING = { "Standard Cockpit",
			"Small Cockpit", "Command Console" };

	/** The constant specifying the short version of the cockpit types. */
	public static final String[] COCKPIT_SHORT_STRING = { "Standard", "Small",
			"Command Console" };

	/** Critical hits constant: CRIT_NONE. */
	public static final int CRIT_NONE = -1;

	/** Critical hits constant: CRIT_CREW. */
	public static final int CRIT_CREW = 0;

	/** Critical hits constant: CRIT_FCS. */
	public static final int CRIT_FCS = 1;

	/** Critical hits constant: CRIT_WEAPON. */
	public static final int CRIT_WEAPON = 2;

	/** Critical hits constant: CRIT_CONTROL. */
	public static final int CRIT_CONTROL = 3;

	/** Critical hits constant: CRIT_SENSOR. */
	public static final int CRIT_SENSOR = 4;

	/** Critical hits constant: CRIT_BOMB. */
	public static final int CRIT_BOMB = 5;

	/** Critical hits constant: CRIT_ENGINE. */
	public static final int CRIT_ENGINE = 6;

	/** Critical hits constant: CRIT_FUEL_TANK. */
	public static final int CRIT_FUEL_TANK = 7;

	/** Critical hits constant: CRIT_AVIONICS. */
	public static final int CRIT_AVIONICS = 8;

	/** Critical hits constant: CRIT_GEAR. */
	public static final int CRIT_GEAR = 9;

	/** Critical hits constant: CRIT_HEATSINK. */
	public static final int CRIT_HEATSINK = 10;

	/** Critical hits constant: CRIT_CARGO. */
	public static final int CRIT_CARGO = 11;

	/** Critical hits constant: CRIT_DOCK_COLLAR. */
	public static final int CRIT_DOCK_COLLAR = 12;

	/** Critical hits constant: CRIT_DOOR. */
	public static final int CRIT_DOOR = 13;

	/** Critical hits constant: CRIT_KF_BOOM. */
	public static final int CRIT_KF_BOOM = 14;

	/** Critical hits constant: CRIT_LIFE_SUPPORT. */
	public static final int CRIT_LIFE_SUPPORT = 15;

	/** Critical hits constant: CRIT_LEFT_THRUSTER. */
	public static final int CRIT_LEFT_THRUSTER = 16;

	/** Critical hits constant: CRIT_RIGHT_THRUSTER. */
	public static final int CRIT_RIGHT_THRUSTER = 17;

	/** Critical hits constant: CRIT_CIC. */
	public static final int CRIT_CIC = 18;

	/** Critical hits constant: CRIT_KF_DRIVE. */
	public static final int CRIT_KF_DRIVE = 19;

	/** Critical hits constant: CRIT_GRAV_DECK. */
	public static final int CRIT_GRAV_DECK = 20;

	/** Critical hits constant: CRIT_WEAPON_BROAD. */
	public static final int CRIT_WEAPON_BROAD = 21;

	/**
	 * The constant for the number of slots. Note: an Aero has no critical slot
	 * limitations so this needs to be larger.
	 */
	private static final int[] NUM_OF_SLOTS = { 100, 100, 100, 100, 100, 100 };

	/** The location abbreviations. */
	protected static final String[] LOCATION_ABBRS = { "NOS", "LWG", "RWG",
			"AFT", "WNG" };

	/** The location names. */
	protected static final String[] LOCATION_NAMES = { "Nose", "Left Wing",
			"Right Wing", "Aft", "Wings" };

	/** The structure type. */
	private int structureType = 0;

	/** The sensor hits. */
	private int sensorHits = 0;

	/** The fcs hits. */
	private int fcsHits = 0;

	/** The engine hits. */
	private int engineHits = 0;

	/** The avionics hits. */
	private int avionicsHits = 0;

	/** The cic hits. */
	private int cicHits = 0;

	/** The gear hit. */
	private boolean gearHit = false;

	/** The structure integrity. */
	private int structIntegrity;

	/** The original structure integrity. */
	private int origStructIntegrity;

	/** The damage threshold. */
	private int damThresh[] = { 0, 0, 0, 0, 0 };

	/** The critical effects. */
	private int potCrit = CRIT_NONE;

	/** The standard damage. */
	private int[] standard_damage = { 0, 0, 0, 0, 0 };

	/** The ignored crew hits. */
	private int ignoredCrewHits = 0;

	/** The cockpit type. */
	private int cockpitType = COCKPIT_STANDARD;

	/** Straight moves from the last turn. */
	private int straightMoves = 0;

	/** The spheroid. */
	private boolean spheroid = false;

	/** The heat sinks. */
	private int heatSinks;

	/** The heat type. */
	private int heatType = HEAT_SINGLE;

	/** Bomb constant for a space bomb attack. */
	public static final String SPACE_BOMB_ATTACK = "SpaceBombAttack";

	/** The maximum bomb points. */
	private int maxBombPoints = 0;

	/** The bomb choices. */
	private int[] bombChoices = new int[BombType.B_NUM];

	/** The fuel. */
	private int fuel = 0;

	/**
	 * Whether or not this Aero has life support.
	 */
	private boolean lifeSupport = true;

	/** Left thrust hits. */
	private int leftThrustHits = 0;

	/** Right thrust hits. */
	private int rightThrustHits = 0;

	/** The out control. */
	private boolean outControl = false;

	/** The out control heat. */
	private boolean outCtrlHeat = false;

	/** Whether or not the move was random. */
	private boolean randomMove = false;

	/** The current velocity used for movements. */
	private int currentVelocity = 0;

	/** The next velocity used for movements. */
	private int nextVelocity = currentVelocity;

	/** The accLast. */
	private boolean accLast = false;

	/** Whether rolled or not. */
	private boolean rolled = false;

	/** Whether or not the maneuver failed. */
	private boolean failedManeuver = false;

	/** The accDecNow. */
	private boolean accDecNow = false;

	/** Whether or not the damage threshold exceeded this turn. */
	private boolean critThresh = false;

	/** The vstol status. */
	private boolean vstol = false;

	/** The capital armor. */
	private int capitalArmor = 0;

	/** The capital armor original. */
	private int capitalArmorOrig = 0;

	/** The fatal threshold. */
	private int fatalThresh = 2;

	/** The current damage. */
	private int currentDamage = 0;

	/** The wings hit. */
	private boolean wingsHit = false;

	/**
	 * The weapon groups. The key is the location: internal name, and the value
	 * is the weapon id
	 */
	Map<String, Integer> weaponGroups = new HashMap<String, Integer>();

	/*
	 * According to the rules if two units of the same type and with the same
	 * velocity are in the same hex, you roll 2d6 randomly to see who is
	 * considered back one step for purposes of targeting. This is a bitch to
	 * do, so instead we assign a large random variable to each Aero unit at the
	 * start of the round and we use that. It works out similarly except that
	 * you don't roll separately for each pair of possibilities. That should
	 * work well enough for our purposes.
	 */
	/** The who first. */
	private int whoFirst = 0;

	/** The eccm roll. */
	private int eccmRoll = 0;



	/**
	 * Returns the location abbreviations.
	 * 
	 * @see megamek.common.Entity#getLocationAbbrs()
	 */
	@Override
	public String[] getLocationAbbrs()
	{
		return LOCATION_ABBRS.clone();
	}



	/**
	 * Returns the location names;
	 * 
	 * @see megamek.common.Entity#getLocationNames()
	 */
	@Override
	public String[] getLocationNames()
	{
		return LOCATION_NAMES.clone();
	}



	/**
	 * Returns this entity's safe thrust, factored for heat, extreme
	 * temperatures, gravity, and bomb load. It first calls the
	 * <code>getOriginalWalkMP</code> method and stores the returned value which
	 * is then used in the rest of the method.
	 * 
	 * @param gravity the gravity of the entity.
	 * @param ignoreheat whether or not to ignore heat.
	 * @return the entity's safe thrust.
	 */
	@Override
	public int getWalkMP(boolean gravity, boolean ignoreheat)
	{
		int walk = getOriginalWalkMP();
		walk = Math.max(0, walk - getCargoMpReduction());

		if (null != game) {
			int weatherMod = game.getPlanetaryConditions()
					.getMovementMods(this);

			if (weatherMod != 0) {
				walk = Math.max(walk + weatherMod, 0);
			}
		}
		// get bomb load
		walk = Math.max(0, walk - (int) Math.ceil(getBombPoints() / 5.0));

		if (hasModularArmor()) {
			walk--;
		}

		return walk;
	}



	/**
	 * Returns the number of locations in the entity.
	 * 
	 * @return the number of locations in the entity.
	 */
	@Override
	public int locations()
	{
		return 5;
	}



	/**
	 * Determines whether this entity change its secondary facing.
	 * 
	 * @see megamek.common.Entity#canChangeSecondaryFacing()
	 */
	@Override
	public boolean canChangeSecondaryFacing()
	{
		return false;
	}



	/**
	 * Determines whether this entity can twist in the given direction.
	 * 
	 * @see megamek.common.Entity#isValidSecondaryFacing(int)
	 */
	@Override
	public boolean isValidSecondaryFacing(int num)
	{
		return false;
	}



	/**
	 * Returns the closest valid secondary facing to the given direction.
	 * 
	 * @see megamek.common.Entity#clipSecondaryFacing(int)
	 */
	@Override
	public int clipSecondaryFacing(int num)
	{
		return num;
	}



	/**
	 * Checks if is out control total.
	 * 
	 * @return true if is out control total.
	 */
	public boolean isOutControlTotal()
	{
		// due to control roll, heat, shut down, or crew unconscious
		return (outControl || shutDown || crew.isUnconscious());
	}



	/**
	 * Checks if is out control.
	 * 
	 * @return true if is out control.
	 */
	public boolean isOutControl()
	{
		return outControl;
	}



	/**
	 * Checks if is out control heat.
	 * 
	 * @return true if is out control heat.
	 */
	public boolean isOutCtrlHeat()
	{
		return outCtrlHeat;
	}



	/**
	 * Checks if is a random move.
	 * 
	 * @return true if is a random move.
	 */
	public boolean isRandomMove()
	{
		return randomMove;
	}



	/**
	 * Checks if is didAccLast.
	 * 
	 * @return true if is didAccLast.
	 */
	public boolean didAccLast()
	{
		return accLast;
	}



	/**
	 * Checks for life support.
	 * 
	 * @return true if has life support.
	 */
	public boolean hasLifeSupport()
	{
		return lifeSupport;
	}



	/**
	 * Sets the life support.
	 * 
	 * @param bool the new life support to set.
	 */
	public void setLifeSupport(boolean bool)
	{
		lifeSupport = bool;
	}



	/**
	 * Checks if is rolled.
	 * 
	 * @return true if is rolled.
	 */
	public boolean isRolled()
	{
		return rolled;
	}



	/**
	 * Sets the out control.
	 * 
	 * @param ocontrol the out control to set.
	 */
	public void setOutControl(boolean ocontrol)
	{
		outControl = ocontrol;
	}



	/**
	 * Sets the out control heat.
	 * 
	 * @param octrlheat the new out control heat to set.
	 */
	public void setOutCtrlHeat(boolean octrlheat)
	{
		outCtrlHeat = octrlheat;
	}



	/**
	 * Sets the random move.
	 * 
	 * @param randmove the new random move.
	 */
	public void setRandomMove(boolean randmove)
	{
		randomMove = randmove;
	}



	/**
	 * Sets the rolled.
	 * 
	 * @param roll the new rolled to set.
	 */
	public void setRolled(boolean roll)
	{
		rolled = roll;
	}



	/**
	 * Sets the accLast.
	 * 
	 * @param bool the accLast to set.
	 */
	public void setAccLast(boolean bool)
	{
		accLast = bool;
	}



	/**
	 * Gets the bomb points. Calls the <code>getBombs</code> method then
	 * determines if each bomb returned by the method has more than zero shots
	 * left. If so, the number of points gets incremented by the bomb cost of
	 * that bomb.
	 * 
	 * @return the bomb points.
	 */
	public int getBombPoints()
	{
		int points = 0;

		for (Mounted bomb : getBombs()) {
			if (bomb.getShotsLeft() > 0) {
				points += BombType.getBombCost(((BombType) bomb.getType())
						.getBombType());
			}
		}

		return points;
	}



	/**
	 * Gets the maximum bomb points.
	 * 
	 * @return the maximum bomb points.
	 */
	public int getMaxBombPoints()
	{
		return maxBombPoints;
	}



	/**
	 * Automatically set the maximum bomb points.
	 */
	public void autoSetMaxBombPoints()
	{
		maxBombPoints = Math.round(getWeight() / 5);
	}



	/**
	 * Gets the bomb choices.
	 * 
	 * @return the bomb choices.
	 */
	public int[] getBombChoices()
	{
		return bombChoices.clone();
	}



	/**
	 * Sets the bomb choices.
	 * 
	 * @param choices the new bomb choices to set.
	 */
	public void setBombChoices(int[] choices)
	{
		if (choices.length == bombChoices.length) {
			bombChoices = choices.clone();
		}
	}



	/**
	 * Sets the who first.
	 */
	public void setWhoFirst()
	{
		whoFirst = Compute.randomInt(500);
	}



	/**
	 * Gets the who first.
	 * 
	 * @return the who first.
	 */
	public int getWhoFirst()
	{
		return whoFirst;
	}



	/**
	 * Gets the current velocity. If an advanced movement is being used, then
	 * the different vectors are totaled and returned.
	 * 
	 * @return the current velocity.
	 */
	public int getCurrentVelocity()
	{
		if (game.useVectorMove()) {
			return getVelocity();
		}

		return currentVelocity;
	}



	/**
	 * Sets the current velocity.
	 * 
	 * @param velocity the new current velocity.
	 */
	public void setCurrentVelocity(int velocity)
	{
		currentVelocity = velocity;
	}



	/**
	 * Gets the next velocity.
	 * 
	 * @return the next velocity.
	 */
	public int getNextVelocity()
	{
		return nextVelocity;
	}



	/**
	 * Sets the next velocity.
	 * 
	 * @param velocity the next velocity to set.
	 */
	public void setNextVelocity(int velocity)
	{
		nextVelocity = velocity;
	}



	/**
	 * Gets the actual current velocity.
	 * 
	 * @return the actual current velocity.
	 */
	public int getCurrentVelocityActual()
	{
		return currentVelocity;
	}



	/**
	 * Gets the critical effects.
	 * 
	 * @return the critical effects.
	 */
	public int getPotCrit()
	{
		return potCrit;
	}



	/**
	 * Sets the critical effects.
	 * 
	 * @param crit the critical effects to set.
	 */
	public void setPotCrit(int crit)
	{
		potCrit = crit;
	}



	/**
	 * Gets the structIntegtiy.
	 * 
	 * @return the structIntegtiy.
	 */
	public int getStructIntegtiy()
	{
		return structIntegrity;
	}



	/**
	 * Gets the original structure integrity.
	 * 
	 * @return the origStructIntegrity.
	 */
	public int getOrigStructIntegtiy()
	{
		return origStructIntegrity;
	}



	/**
	 * Gets the capital armor.
	 * 
	 * @return the capital armor.
	 */
	public int getCapitalArmor()
	{
		return capitalArmor;
	}



	/**
	 * Sets the cap armor.
	 * 
	 * @param capAmor the new cap armor
	 */
	public void setCapitalArmor(int capAmor)
	{
		capitalArmor = capAmor;
	}



	/**
	 * // * Gets the original capital armor.
	 * 
	 * @return the original capital armor.
	 */
	public int getCapitalArmorOrig()
	{
		return capitalArmorOrig;
	}



	/**
	 * Gets the fatal threshold.
	 * 
	 * @return the fatal threshold.
	 */
	public int getFatalThresh()
	{
		return fatalThresh;
	}



	/**
	 * Gets the current damage.
	 * 
	 * @return the current damage.
	 */
	public int getCurrentDamage()
	{
		return currentDamage;
	}



	/**
	 * Sets the current damage.
	 * 
	 * @param damage the new current damage
	 */
	public void setCurrentDamage(int damage)
	{
		currentDamage = damage;
	}



	/**
	 * Sets the <code>structIntegrity</code> and the
	 * <code>origStructIntegrity</code>.
	 * 
	 * @param integrity the new structIntegrity to set.
	 */
	public void setOrigStructIntegtiy(int integrity)
	{
		origStructIntegrity = integrity;
		structIntegrity = integrity;
	}



	/**
	 * Automatically set the <code>origStructIntegrity</code>. First determines
	 * the weight; then determines the thrust by calling the
	 * <code>getOriginalWalkMP</code> method. Then calls
	 * <code>initializeSI</code> with the weight and thrust as parameters.
	 */
	public void autoSetStructIntegtiy()
	{
		int siweight = (int) Math.floor(weight / 10.0);
		int sithrust = getOriginalWalkMP();
		initStructIntegrity(Math.max(siweight, sithrust));
	}



	/**
	 * Automatically set the capital armor.
	 */
	public void autoSetCapitalArmor()
	{
		capitalArmorOrig = calcCapital(getTotalOArmor());
		capitalArmor = calcCapital(getTotalArmor());
	}



	/**
	 * Calculates the number to automatically be set as the capital armor.
	 * 
	 * @return an integer to be set as the capital armor.
	 */
	private int calcCapital(int num)
	{
		return (int) Math.round(num / 10.0);
	}



	/**
	 * Automatically set the fatal threshold.
	 */
	public void autoSetFatalThresh()
	{
		fatalThresh = Math.max(2, (int) Math.ceil(capitalArmor / 4.0));
	}



	/**
	 * Initializes the <code>structIntegrity</code> and
	 * <code>orig_strucIntegrity</code>.
	 * 
	 * @param value the value to set the structIntegrity to.
	 */
	public void initStructIntegrity(int value)
	{
		origStructIntegrity = value;
		setStructIntegrity(value);
	}



	/**
	 * Sets the <code>structIntegrity</code>.
	 * 
	 * @param integrity the <code>structIntegrity</code> to set.
	 */
	public void setStructIntegrity(int integrity)
	{
		structIntegrity = integrity;
	}



	/**
	 * Gets the sensor hits.
	 * 
	 * @return the sensor hits.
	 */
	public int getSensorHits()
	{
		return sensorHits;
	}



	/**
	 * Sets the sensor hits. Calls <code>determineHits</code> in order to
	 * determine the number of hits.
	 * 
	 * @param hits the sensor hits to set.
	 */
	public void setSensorHits(int hits)
	{
		sensorHits = determineHits(hits);
	}



	/**
	 * Determines the number of hits to set. The maximum number is 3; if the
	 * number entered is greater, then the max gets store.
	 * 
	 * @param hits the number of hits.
	 * @return the number of hits to set.
	 */
	private int determineHits(int hits)
	{
		int numHits = hits;
		int max = getMaxEngineHits();

		if (numHits > max) {
			numHits = max;
		}

		return numHits;
	}



	/**
	 * Gets the fcs hits.
	 * 
	 * @return the fcs hits
	 */
	public int getFCSHits()
	{
		return fcsHits;
	}



	/**
	 * Sets the fcs hits. Calls <code>determineHits</code> in order to determine
	 * the number of hits.
	 * 
	 * @param hits the fcs hits to set.
	 */
	public void setFCSHits(int hits)
	{
		fcsHits = determineHits(hits);
	}



	/**
	 * Sets the cic hits.
	 * 
	 * @param hits the cic hits to set.
	 */
	public void setCICHits(int hits)
	{
		cicHits = hits;
	}



	/**
	 * Gets the cic hits.
	 * 
	 * @return the cic hits.
	 */
	public int getCICHits()
	{
		return cicHits;
	}



	/**
	 * Sets the ignored crew hits.
	 * 
	 * @param hits the ignored crew hits to set.
	 */
	public void setIgnoredCrewHits(int hits)
	{
		ignoredCrewHits = hits;
	}



	/**
	 * Gets the ignored crew hits.
	 * 
	 * @return the ignored crew hits.
	 */
	public int getIgnoredCrewHits()
	{
		return ignoredCrewHits;
	}



	/**
	 * Gets the engine hits.
	 * 
	 * @return the engine hits.
	 */
	public int getEngineHits()
	{
		return engineHits;
	}



	/**
	 * Sets the engine hits.
	 * 
	 * @param hits the new engine hits.
	 */
	public void setEngineHits(int hits)
	{
		engineHits = hits;
	}



	/**
	 * Gets the avionics hits.
	 * 
	 * @return the avionics hits.
	 */
	public int getAvionicsHits()
	{
		return avionicsHits;
	}



	/**
	 * Sets the avionics hits.
	 * 
	 * @param hits the new avionics hits.
	 */
	public void setAvionicsHits(int hits)
	{
		avionicsHits = hits;
	}



	/**
	 * Checks if is a gear hit.
	 * 
	 * @return true if is a gear hit.
	 */
	public boolean isGearHit()
	{
		return gearHit;
	}



	/**
	 * Sets the gear hit.
	 * 
	 * @param hit the new gear hit.
	 */
	public void setGearHit(boolean hit)
	{
		gearHit = hit;
	}



	/**
	 * Sets the heat sinks.
	 * 
	 * @param heatsink the new heat sinks.
	 */
	public void setHeatSinks(int heatsink)
	{
		heatSinks = heatsink;
	}



	/**
	 * Gets the heat sinks.
	 * 
	 * @return the heat sinks.
	 */
	public int getHeatSinks()
	{
		return heatSinks;
	}



	/**
	 * Sets the heat type.
	 * 
	 * @param hstype the heat type to set.
	 */
	public void setHeatType(int hstype)
	{
		heatType = hstype;
	}



	/**
	 * Sets the left thrust hits.
	 * 
	 * @param hits the left thrust hits to set.
	 */
	public void setLeftThrustHits(int hits)
	{
		leftThrustHits = hits;
	}



	/**
	 * Gets the left thrust hits.
	 * 
	 * @return the left thrust hits.
	 */
	public int getLeftThrustHits()
	{
		return leftThrustHits;
	}



	/**
	 * Sets the right thrust hits.
	 * 
	 * @param hits the right thrust hits to set.
	 */
	public void setRightThrustHits(int hits)
	{
		rightThrustHits = hits;
	}



	/**
	 * Gets the right thrust hits.
	 * 
	 * @return the right thrust hits.
	 */
	public int getRightThrustHits()
	{
		return rightThrustHits;
	}



	/**
	 * Gets the fuel.
	 * 
	 * @return the fuel.
	 */
	public int getFuel()
	{
		return fuel;
	}



	/**
	 * Sets the fuel.
	 * 
	 * @param gas the new fuel.
	 */
	public void setFuel(int gas)
	{
		fuel = gas;
	}



	/**
	 * Gets the heat type.
	 * 
	 * @return the heat type.
	 */
	public int getHeatType()
	{
		return heatType;
	}



	/**
	 * Checks if was critical threshold.
	 * 
	 * @return true if was critical threshold.
	 */
	public boolean wasCritThresh()
	{
		return critThresh;
	}



	/**
	 * Sets the critical threshold.
	 * 
	 * @param bool the critical threshold to set.
	 */
	public void setCritThresh(boolean bool)
	{
		critThresh = bool;
	}



	/**
	 * Immobilize. Sets the mbImmobileHit as true and calls
	 * <code>setOriginalWalkMP</code>.
	 */
	public void immobilize()
	{
		mbImmobileHit = true;
		setOriginalWalkMP(0);
	}



	/**
	 * Determines if this entity was shut down or if is the crew was
	 * unconscious. Returns false since an aero is never immobile.
	 * 
	 * @see megamek.common.Entity#isImmobile()
	 */
	@Override
	public boolean isImmobile()
	{
		return false;
	}



	/**
	 * Applies any damage that the entity has suffered.
	 * 
	 * @see megamek.common.Entity#applyDamage()
	 */
	@Override
	public void applyDamage()
	{
		mbImmobile |= mbImmobileHit;
	}



	/**
	 * Sets a new round. This is done by setting the <code>critThresh</code>,
	 * <code>failedManeuver</code> and <code>accDecNow</code> as false then
	 * calling <code>updateBays</code>. Then the recovery turn and atmosphere
	 * gets updated (if in either of those) as well as the velocity. The
	 * variable damage threshold is then automatically set and the eccm bonus
	 * gets reset. If out of control, it gets reset and gets who's next;
	 * 
	 * @see megamek.common.Entity#newRound(int)
	 */
	@Override
	public void newRound(int roundNumber)
	{
		super.newRound(roundNumber);

		setCritThresh(false);
		setFailedManeuver(false);
		setAccDecNow(false);

		updateBays();

		// update recovery turn if in recovery
		if (getRecoveryTurn() > 0) {
			setRecoveryTurn(getRecoveryTurn() - 1);
		}

		// if in atmosphere, then halve next turn's velocity
		if (game.getBoard().inAtmosphere() && isDeployed() && (roundNumber > 0)) {
			setNextVelocity((int) Math.floor(getNextVelocity() / 2.0));
		}

		// update velocity
		setCurrentVelocity(getNextVelocity());

		// if using variable damage thresholds then autoset them
		if (game.getOptions().booleanOption("variable_damage_thresh")) {
			autoSetThresh();
		}

		// if they are out of control due to heat, then apply this and reset
		if (isOutCtrlHeat()) {
			setOutControl(true);
			setOutCtrlHeat(false);
		}

		// reset eccm bonus
		setECCMRoll(Compute.d6(2));
		setWhoFirst();

	}



	/**
	 * Returns the name of the type of movement used. This is tank-specific.
	 * 
	 * @param mtype the movement type.
	 * @return the movement string representation of the movement used.
	 */
	@Override
	public String getMovementString(int mtype)
	{
		String movement;

		switch (mtype) {
		case IEntityMovementType.MOVE_SKID:
			movement = "Skidded";
			break;
		case IEntityMovementType.MOVE_NONE:
			movement = "None";
			break;
		case IEntityMovementType.MOVE_WALK:
			movement = "Cruised";
			break;
		case IEntityMovementType.MOVE_RUN:
			movement = "Flanked";
			break;
		case IEntityMovementType.MOVE_JUMP:
			movement = "Jumped";
			break;
		case IEntityMovementType.MOVE_SAFE_THRUST:
			movement = "Safe Thrust";
			break;
		case IEntityMovementType.MOVE_OVER_THRUST:
			movement = "Over Thrust";
			break;
		default:
			movement = "Unknown!";
			break;
		}

		return movement;
	}



	/**
	 * Returns the abbreviation of the type of movement used. This is
	 * tank-specific.
	 * 
	 * @param mtype the movement type.
	 * @return the abbreviation of the movement used.
	 */
	@Override
	public String getMovementAbbr(int mtype)
	{
		String abbr;

		switch (mtype) {
		case IEntityMovementType.MOVE_NONE:
			abbr = "N";
			break;
		case IEntityMovementType.MOVE_SAFE_THRUST:
			abbr = "S";
			break;
		case IEntityMovementType.MOVE_OVER_THRUST:
			abbr = "O";
			break;
		default:
			abbr = "?";
			break;
		}

		return abbr;
	}



	/**
	 * Determines whether this location has rear armor.
	 * 
	 * @see megamek.common.Entity#hasRearArmor(int)
	 */
	@Override
	public boolean hasRearArmor(int loc)
	{
		return false;
	}



	/**
	 * Returns the Compute.ARC that the weapon fires into.
	 * 
	 * @param weapon the weapon.
	 * @return the weapon arc.
	 */
	// need to figure out aft-pointed wing weapons need to figure out new arcs
	@Override
	public int getWeaponArc(int weapon)
	{
		final Mounted mounted = getEquipment(weapon);

		if (mounted.getType().hasFlag(WeaponType.F_SPACE_BOMB)) {
			return Compute.ARC_360;
		}

		int arc = Compute.ARC_NOSE;

		switch (mounted.getLocation()) {
		case LOC_NOSE:
			arc = Compute.ARC_NOSE;
			break;
		case LOC_RWING:
			if (mounted.isRearMounted()) {
				arc = Compute.ARC_RWINGA;
			}
			else {
				arc = Compute.ARC_RWING;
			}
			break;
		case LOC_LWING:
			if (mounted.isRearMounted()) {
				arc = Compute.ARC_LWINGA;
			}
			else {
				arc = Compute.ARC_LWING;
			}
			break;
		case LOC_AFT:
			arc = Compute.ARC_AFT;
			break;
		case LOC_WINGS:
			arc = Compute.ARC_NOSE;
			break;
		default:
			arc = Compute.ARC_360;
		}

		return rollArcs(arc);
	}



	/**
	 * Switches certain arcs due to rolling.
	 * 
	 * @param arc the arc to switch.
	 * @return the switched arc.
	 */
	public int rollArcs(int arc)
	{
		if (isRolled()) {
			if (arc == Compute.ARC_LWING) {
				return Compute.ARC_RWING;
			}
			else if (arc == Compute.ARC_RWING) {
				return Compute.ARC_LWING;
			}
			else if (arc == Compute.ARC_LWINGA) {
				return Compute.ARC_RWINGA;
			}
			else if (arc == Compute.ARC_RWINGA) {
				return Compute.ARC_LWINGA;
			}
			else if (arc == Compute.ARC_LEFTSIDE_SPHERE) {
				return Compute.ARC_RIGHTSIDE_SPHERE;
			}
			else if (arc == Compute.ARC_RIGHTSIDE_SPHERE) {
				return Compute.ARC_LEFTSIDE_SPHERE;
			}
			else if (arc == Compute.ARC_LEFTSIDEA_SPHERE) {
				return Compute.ARC_RIGHTSIDEA_SPHERE;
			}
			else if (arc == Compute.ARC_RIGHTSIDEA_SPHERE) {
				return Compute.ARC_LEFTSIDEA_SPHERE;
			}
			else if (arc == Compute.ARC_LEFT_BROADSIDE) {
				return Compute.ARC_RIGHT_BROADSIDE;
			}
			else if (arc == Compute.ARC_RIGHT_BROADSIDE) {
				return Compute.ARC_LEFT_BROADSIDE;
			}
		}

		return arc;
	}



	/**
	 * Determines whether this weapon fires into the secondary facing arc. If
	 * false, assume it fires into the primary.
	 * 
	 * @param weaponId the weapon id.
	 * @return true if the weapon fires into the secondary facing arc.
	 */
	@Override
	public boolean isSecondaryArcWeapon(int weaponId)
	{
		// just leave true for now in case we implement rolls or
		// newtonian movement this way
		return true;
	}



	/**
	 * Rolls up a hit location.
	 * 
	 * @param table the table.
	 * @param side the side.
	 * @param aimedLocation the location aimed at.
	 * @param aimingMode the aiming mode.
	 * @return the hit data of the roll.
	 */
	@Override
	public HitData rollHitLocation(int table, int side, int aimedLocation,
			int aimingMode)
	{
		return rollHitLocation(table, side);
	}



	/**
	 * Rolls up a hit location. Unlike other units, ASFs determine potential
	 * crits based on the to-hit roll, so this value first gets stored. This is
	 * determined by the roll and the side.
	 * 
	 * @see megamek.common.Entity#rollHitLocation(int, int)
	 */
	@Override
	public HitData rollHitLocation(int table, int side)
	{
		int roll = Compute.d6(2);

		// first check for above/below
		if ((table == ToHitData.HIT_ABOVE) || (table == ToHitData.HIT_BELOW)) {
			// have to decide which wing
			int wingloc = LOC_RWING;
			int wingroll = Compute.d6(1);

			if (wingroll > 3) {
				wingloc = LOC_LWING;
			}

			switch (roll) {
			case 2:
				setPotCrit(CRIT_WEAPON);
				return new HitData(LOC_NOSE, false, HitData.EFFECT_NONE);
			case 3:
				setPotCrit(CRIT_GEAR);
				return new HitData(wingloc, false, HitData.EFFECT_NONE);
			case 4:
				setPotCrit(CRIT_SENSOR);
				return new HitData(LOC_NOSE, false, HitData.EFFECT_NONE);
			case 5:
				setPotCrit(CRIT_CREW);
				return new HitData(LOC_NOSE, false, HitData.EFFECT_NONE);
			case 6:
				setPotCrit(CRIT_WEAPON);
				return new HitData(wingloc, false, HitData.EFFECT_NONE);
			case 7:
				setPotCrit(CRIT_AVIONICS);
				return new HitData(LOC_NOSE, false, HitData.EFFECT_NONE);
			case 8:
				setPotCrit(CRIT_WEAPON);
				return new HitData(wingloc, false, HitData.EFFECT_NONE);
			case 9:
				setPotCrit(CRIT_CONTROL);
				return new HitData(LOC_AFT, false, HitData.EFFECT_NONE);
			case 10:
				setPotCrit(CRIT_ENGINE);
				return new HitData(LOC_AFT, false, HitData.EFFECT_NONE);
			case 11:
				setPotCrit(CRIT_GEAR);
				return new HitData(wingloc, false, HitData.EFFECT_NONE);
			case 12:
				setPotCrit(CRIT_WEAPON);
				return new HitData(LOC_AFT, false, HitData.EFFECT_NONE);
			}
		}

		if (side == ToHitData.SIDE_FRONT) {
			// normal front hits
			switch (roll) {
			case 2:
				setPotCrit(CRIT_WEAPON);
				return new HitData(LOC_NOSE, false, HitData.EFFECT_NONE);
			case 3:
				setPotCrit(CRIT_SENSOR);
				return new HitData(LOC_NOSE, false, HitData.EFFECT_NONE);
			case 4:
				setPotCrit(CRIT_HEATSINK);
				return new HitData(LOC_RWING, false, HitData.EFFECT_NONE);
			case 5:
				setPotCrit(CRIT_WEAPON);
				return new HitData(LOC_RWING, false, HitData.EFFECT_NONE);
			case 6:
				setPotCrit(CRIT_AVIONICS);
				return new HitData(LOC_NOSE, false, HitData.EFFECT_NONE);
			case 7:
				setPotCrit(CRIT_CONTROL);
				return new HitData(LOC_NOSE, false, HitData.EFFECT_NONE);
			case 8:
				setPotCrit(CRIT_FCS);
				return new HitData(LOC_NOSE, false, HitData.EFFECT_NONE);
			case 9:
				setPotCrit(CRIT_WEAPON);
				return new HitData(LOC_LWING, false, HitData.EFFECT_NONE);
			case 10:
				setPotCrit(CRIT_HEATSINK);
				return new HitData(LOC_LWING, false, HitData.EFFECT_NONE);
			case 11:
				setPotCrit(CRIT_GEAR);
				return new HitData(LOC_NOSE, false, HitData.EFFECT_NONE);
			case 12:
				setPotCrit(CRIT_WEAPON);
				return new HitData(LOC_NOSE, false, HitData.EFFECT_NONE);
			}
		}
		else if (side == ToHitData.SIDE_LEFT) {
			// normal left-side hits
			switch (roll) {
			case 2:
				setPotCrit(CRIT_WEAPON);
				return new HitData(LOC_NOSE, false, HitData.EFFECT_NONE);
			case 3:
				setPotCrit(CRIT_GEAR);
				return new HitData(LOC_LWING, false, HitData.EFFECT_NONE);
			case 4:
				setPotCrit(CRIT_SENSOR);
				return new HitData(LOC_NOSE, false, HitData.EFFECT_NONE);
			case 5:
				setPotCrit(CRIT_CREW);
				return new HitData(LOC_NOSE, false, HitData.EFFECT_NONE);
			case 6:
				setPotCrit(CRIT_WEAPON);
				return new HitData(LOC_LWING, false, HitData.EFFECT_NONE);
			case 7:
				setPotCrit(CRIT_AVIONICS);
				return new HitData(LOC_LWING, false, HitData.EFFECT_NONE);
			case 8:
				setPotCrit(CRIT_BOMB);
				return new HitData(LOC_LWING, false, HitData.EFFECT_NONE);
			case 9:
				setPotCrit(CRIT_CONTROL);
				return new HitData(LOC_AFT, false, HitData.EFFECT_NONE);
			case 10:
				setPotCrit(CRIT_ENGINE);
				return new HitData(LOC_AFT, false, HitData.EFFECT_NONE);
			case 11:
				setPotCrit(CRIT_GEAR);
				return new HitData(LOC_LWING, false, HitData.EFFECT_NONE);
			case 12:
				setPotCrit(CRIT_WEAPON);
				return new HitData(LOC_AFT, false, HitData.EFFECT_NONE);
			}
		}
		else if (side == ToHitData.SIDE_RIGHT) {
			// normal right-side hits
			switch (roll) {
			case 2:
				setPotCrit(CRIT_WEAPON);
				return new HitData(LOC_NOSE, false, HitData.EFFECT_NONE);
			case 3:
				setPotCrit(CRIT_GEAR);
				return new HitData(LOC_RWING, false, HitData.EFFECT_NONE);
			case 4:
				setPotCrit(CRIT_SENSOR);
				return new HitData(LOC_NOSE, false, HitData.EFFECT_NONE);
			case 5:
				setPotCrit(CRIT_CREW);
				return new HitData(LOC_NOSE, false, HitData.EFFECT_NONE);
			case 6:
				setPotCrit(CRIT_WEAPON);
				return new HitData(LOC_RWING, false, HitData.EFFECT_NONE);
			case 7:
				setPotCrit(CRIT_AVIONICS);
				return new HitData(LOC_RWING, false, HitData.EFFECT_NONE);
			case 8:
				setPotCrit(CRIT_BOMB);
				return new HitData(LOC_RWING, false, HitData.EFFECT_NONE);
			case 9:
				setPotCrit(CRIT_CONTROL);
				return new HitData(LOC_AFT, false, HitData.EFFECT_NONE);
			case 10:
				setPotCrit(CRIT_ENGINE);
				return new HitData(LOC_AFT, false, HitData.EFFECT_NONE);
			case 11:
				setPotCrit(CRIT_GEAR);
				return new HitData(LOC_RWING, false, HitData.EFFECT_NONE);
			case 12:
				setPotCrit(CRIT_WEAPON);
				return new HitData(LOC_AFT, false, HitData.EFFECT_NONE);
			}
		}
		else if (side == ToHitData.SIDE_REAR) {
			// normal aft hits
			switch (roll) {
			case 2:
				setPotCrit(CRIT_WEAPON);
				return new HitData(LOC_AFT, false, HitData.EFFECT_NONE);
			case 3:
				setPotCrit(CRIT_HEATSINK);
				return new HitData(LOC_AFT, false, HitData.EFFECT_NONE);
			case 4:
				setPotCrit(CRIT_FUEL_TANK);
				return new HitData(LOC_RWING, false, HitData.EFFECT_NONE);
			case 5:
				setPotCrit(CRIT_WEAPON);
				return new HitData(LOC_RWING, false, HitData.EFFECT_NONE);
			case 6:
				setPotCrit(CRIT_ENGINE);
				return new HitData(LOC_AFT, false, HitData.EFFECT_NONE);
			case 7:
				setPotCrit(CRIT_CONTROL);
				return new HitData(LOC_AFT, false, HitData.EFFECT_NONE);
			case 8:
				setPotCrit(CRIT_ENGINE);
				return new HitData(LOC_AFT, false, HitData.EFFECT_NONE);
			case 9:
				setPotCrit(CRIT_WEAPON);
				return new HitData(LOC_LWING, false, HitData.EFFECT_NONE);
			case 10:
				setPotCrit(CRIT_FUEL_TANK);
				return new HitData(LOC_LWING, false, HitData.EFFECT_NONE);
			case 11:
				setPotCrit(CRIT_HEATSINK);
				return new HitData(LOC_AFT, false, HitData.EFFECT_NONE);
			case 12:
				setPotCrit(CRIT_WEAPON);
				return new HitData(LOC_AFT, false, HitData.EFFECT_NONE);
			}
		}

		return new HitData(LOC_NOSE, false, HitData.EFFECT_NONE);
	}



	/**
	 * Gets the location that excess damage transfers to.
	 * 
	 * @param hit the hit.
	 * @return the transfer location.
	 */
	@Override
	public HitData getTransferLocation(HitData hit)
	{
		return new HitData(LOC_DESTROYED);
	}



	/**
	 * Gets the location that is destroyed recursively.
	 * 
	 * @param loc the location to destroy.
	 * @return the dependent location.
	 */
	@Override
	public int getDependentLocation(int loc)
	{
		return LOC_NONE;
	}



	/**
	 * Calculates the battle value of this entity.
	 * 
	 * @see megamek.common.Entity#calculateBattleValue()
	 */
	@Override
	public int calculateBattleValue()
	{
		return calculateBattleValue(false, false);
	}



	/**
	 * Calculates the battle value of this mech. If the parameter is true, then
	 * the battle value for c3 will be added whether the mech is currently part
	 * of a network or not. This should be overwritten if necessary.
	 * 
	 * @see megamek.common.Entity#calculateBattleValue(boolean, boolean)
	 */
	@Override
	public int calculateBattleValue(boolean ignoreC3, boolean ignorePilot)
	{
		double dbv = 0; // defensive battle value
		double obv = 0; // offensive battleValue
		int modularArmor = 0;

		for (Mounted mounted : getEquipment()) {
			if ((mounted.getType() instanceof MiscType) &&
					mounted.getType().hasFlag(MiscType.F_MODULAR_ARMOR)) {
				modularArmor += mounted.getBaseDamageCapacity() -
						mounted.getDamageTaken();
			}
		}

		dbv += (getTotalArmor() + modularArmor) * 2.5;
		dbv += getStructIntegtiy() * 2.0;

		// add defensive equipment
		double dEquipmentBV = 0;

		for (Mounted mounted : getEquipment()) {
			EquipmentType etype = mounted.getType();

			// don't count destroyed equipment
			if (mounted.isDestroyed()) {
				continue;
			}

			if (((etype instanceof WeaponType) && (etype
					.hasFlag(WeaponType.F_AMS))) ||
					((etype instanceof AmmoType) && (((AmmoType) etype)
							.getAmmoType() == AmmoType.T_AMS)) ||
					((etype instanceof AmmoType) && (((AmmoType) etype)
							.getAmmoType() == AmmoType.T_SCREEN_LAUNCHER)) ||
					((etype instanceof WeaponType) && (((WeaponType) etype)
							.getAtClass() == WeaponType.CLASS_SCREEN))) {
				dEquipmentBV += etype.getBattleValue(this);
			}
		}
		dbv += dEquipmentBV;

		// subtract for explosive ammo
		double ammoPenalty = 0;
		// need to keep track of any ammo type already used
		boolean[][] ammoTypesUsed = new boolean[AmmoType.NUM_TYPES][41];

		for (Mounted mounted : getEquipment()) {
			int loc = mounted.getLocation();
			int toSubtract = 15;
			EquipmentType etype = mounted.getType();

			// only count explosive ammo
			if (!etype.isExplosive()) {
				continue;
			}

			// do not count weapon groups
			if (mounted.isWeaponGroup()) {
				continue;
			}

			// CASE means no subtraction
			// clan aeros automatically have CASE
			if (hasCase() || isClan()) {
				continue;
			}

			// don't count oneshot ammo
			if (loc == LOC_NONE) {
				continue;
			}

			// gauss rifles only subtract 1 point per slot
			if (etype instanceof GaussWeapon) {
				toSubtract = 1;
			}

			// only ammo counts from here on out
			if ((etype instanceof AmmoType)) {
				AmmoType aType = (AmmoType) etype;
				// empty ammo shouldn't count
				if (mounted.getShotsLeft() == 0) {
					continue;
				}

				// only subtract once for each weapon we identify by matching
				// via the ammoType var and the racksize
				if (ammoTypesUsed[aType.ammoType][aType.getRackSize()]) {
					continue;
				}
				ammoTypesUsed[aType.ammoType][aType.getRackSize()] = true;
			}
			ammoPenalty += toSubtract;
		}
		dbv = Math.max(1, dbv - ammoPenalty);

		// unit type multiplier
		dbv *= getBVTypeModifier();

		// calculate heat efficiency
		int aeroHeatEfficiency = 6 + getHeatCapacity();

		// total up maximum heat generated
		// and add up BVs for ammo-using weapon types for excessive ammo rule
		Map<String, Double> weaponsForExcessiveAmmo = new HashMap<String, Double>();
		double maximumHeat = 0;

		for (Mounted mounted : getTotalWeaponList()) {
			WeaponType wtype = (WeaponType) mounted.getType();
			double weaponHeat = wtype.getHeat();

			// only count non-damaged equipment
			if (mounted.isMissing() || mounted.isHit() ||
					mounted.isDestroyed() || mounted.isBreached()) {
				continue;
			}

			// do not count weapon groups
			if (mounted.isWeaponGroup()) {
				continue;
			}

			// one shot weapons count 1/4
			if ((wtype.getAmmoType() == AmmoType.T_ROCKET_LAUNCHER) ||
					wtype.hasFlag(WeaponType.F_ONESHOT)) {
				weaponHeat *= 0.25;
			}

			// double heat for ultras
			if ((wtype.getAmmoType() == AmmoType.T_AC_ULTRA) ||
					(wtype.getAmmoType() == AmmoType.T_AC_ULTRA_THB)) {
				weaponHeat *= 2;
			}

			// Six times heat for RAC
			if (wtype.getAmmoType() == AmmoType.T_AC_ROTARY) {
				weaponHeat *= 6;
			}

			// half heat for streaks
			if ((wtype.getAmmoType() == AmmoType.T_SRM_STREAK) ||
					(wtype.getAmmoType() == AmmoType.T_MRM_STREAK) ||
					(wtype.getAmmoType() == AmmoType.T_LRM_STREAK)) {
				weaponHeat *= 0.5;
			}
			maximumHeat += weaponHeat;

			// add up BV of ammo-using weapons for each type of weapon,
			// to compare with ammo BV later for excessive ammo BV rule
			if (!((wtype.hasFlag(WeaponType.F_ENERGY) && !(wtype.getAmmoType() == AmmoType.T_PLASMA)) ||
					wtype.hasFlag(WeaponType.F_ONESHOT) ||
					wtype.hasFlag(WeaponType.F_INFANTRY) || (wtype
						.getAmmoType() == AmmoType.T_NA))) {
				String key = wtype.getAmmoType() + ":" + wtype.getRackSize();

				if (!weaponsForExcessiveAmmo.containsKey(key)) {
					weaponsForExcessiveAmmo.put(key, wtype.getBattleValue(this));
				}
				else {
					weaponsForExcessiveAmmo.put(key, wtype.getBattleValue(this) +
							weaponsForExcessiveAmmo.get(key));
				}
			}
		}

		double weaponBV = 0;
		boolean hasTargComp = hasTargComp();
		// first, add up front-faced and rear-faced unmodified BV,
		// to know wether front- or rear faced BV should be halved
		double bvFront = 0, bvRear = 0;
		ArrayList<Mounted> weapons = getTotalWeaponList();

		for (Mounted weapon : weapons) {
			WeaponType wtype = (WeaponType) weapon.getType();
			double dBV = wtype.getBattleValue(this);

			// don't count destroyed equipment
			if (weapon.isDestroyed()) {
				continue;
			}

			// don't count AMS, it's defensive
			if (wtype.hasFlag(WeaponType.F_AMS)) {
				continue;
			}

			// don't count screen launchers, they are defensive
			if (wtype.getAtClass() == WeaponType.CLASS_SCREEN) {
				continue;
			}

			// do not count weapon groups
			if (weapon.isWeaponGroup()) {
				continue;
			}

			// calc MG Array here:
			if (wtype.hasFlag(WeaponType.F_MGA)) {
				double mgaBV = 0;
				for (Mounted possibleMG : getTotalWeaponList()) {
					if (possibleMG.getType().hasFlag(WeaponType.F_MG) &&
							(possibleMG.getLocation() == weapon.getLocation())) {
						mgaBV += possibleMG.getType().getBattleValue(this);
					}
				}
				dBV = mgaBV * 0.67;
			}

			if (weapon.isRearMounted() || (weapon.getLocation() == LOC_AFT)) {
				bvRear += dBV;
			}
			else {
				bvFront += dBV;
			}
		}

		boolean halveRear = true;
		if (bvFront <= bvRear) {
			halveRear = false;
		}

		if (maximumHeat <= aeroHeatEfficiency) {
			// count all weapons equal, adjusting for rear-firing and excessive
			// ammo
			for (Mounted weapon : getTotalWeaponList()) {
				WeaponType wtype = (WeaponType) weapon.getType();
				double dBV = wtype.getBattleValue(this);

				// don't count destroyed equipment
				if (weapon.isDestroyed()) {
					continue;
				}

				// don't count AMS, it's defensive
				if (wtype.hasFlag(WeaponType.F_AMS)) {
					continue;
				}

				// don't count screen launchers, they are defensive
				if (wtype.getAtClass() == WeaponType.CLASS_SCREEN) {
					continue;
				}
				// do not count weapon groups
				if (weapon.isWeaponGroup()) {
					continue;
				}

				// calc MG Array here:
				if (wtype.hasFlag(WeaponType.F_MGA)) {
					double mgaBV = 0;
					for (Mounted possibleMG : getTotalWeaponList()) {
						if (possibleMG.getType().hasFlag(WeaponType.F_MG) &&
								(possibleMG.getLocation() == weapon
										.getLocation())) {
							mgaBV += possibleMG.getType().getBattleValue(this);
						}
					}
					dBV = mgaBV * 0.67;
				}

				// and we'll add the tcomp here too
				if (wtype.hasFlag(WeaponType.F_DIRECT_FIRE)) {
					if (hasTargComp) {
						dBV *= 1.25;
					}
				}

				// artemis bumps up the value
				if (weapon.getLinkedBy() != null) {
					Mounted mLinker = weapon.getLinkedBy();
					if ((mLinker.getType() instanceof MiscType) &&
							mLinker.getType().hasFlag(MiscType.F_ARTEMIS)) {
						dBV *= 1.2;
					}
					if ((mLinker.getType() instanceof MiscType) &&
							mLinker.getType().hasFlag(MiscType.F_APOLLO)) {
						dBV *= 1.15;
					}
				}

				// half for being rear mounted (or front mounted, when more
				// rear-than front-mounted un-modded BV
				if (((weapon.isRearMounted() || (weapon.getLocation() == LOC_AFT)) && halveRear) ||
						(!(weapon.isRearMounted() || (weapon.getLocation() == LOC_AFT)) && !halveRear)) {
					dBV /= 2;
				}
				weaponBV += dBV;
			}
		}
		else {
			// this will count heat-generating weapons at full modified BV
			// until heatefficiency is reached or passed with one weapon

			// here we store the modified BV and heat of all heat-using
			// weapons, to later be sorted by BV
			ArrayList<double[]> heatBVs = new ArrayList<double[]>();
			// BVs of non-heat-using weapons
			ArrayList<Double> nonHeatBVs = new ArrayList<Double>();

			// loop through weapons, calc their modified BV
			for (Mounted weapon : weapons) {
				WeaponType wtype = (WeaponType) weapon.getType();
				double dBV = wtype.getBattleValue(this);

				// don't count destroyed equipment
				if (weapon.isDestroyed()) {
					continue;
				}

				// don't count AMS, it's defensive
				if (wtype.hasFlag(WeaponType.F_AMS)) {
					continue;
				}

				// don't count screen launchers, they are defensive
				if (wtype.getAtClass() == WeaponType.CLASS_SCREEN) {
					continue;
				}

				// do not count weapon groups
				if (weapon.isWeaponGroup()) {
					continue;
				}

				// calc MG Array here:
				if (wtype.hasFlag(WeaponType.F_MGA)) {
					double mgaBV = 0;
					for (Mounted possibleMG : getTotalWeaponList()) {
						if (possibleMG.getType().hasFlag(WeaponType.F_MG) &&
								(possibleMG.getLocation() == weapon
										.getLocation())) {
							mgaBV += possibleMG.getType().getBattleValue(this);
						}
					}
					dBV = mgaBV * 0.67;
				}

				// and we'll add the tcomp here too
				if (wtype.hasFlag(WeaponType.F_DIRECT_FIRE) && hasTargComp) {
					dBV *= 1.25;
				}

				// artemis bumps up the value
				if (weapon.getLinkedBy() != null) {
					Mounted mLinker = weapon.getLinkedBy();
					if ((mLinker.getType() instanceof MiscType) &&
							mLinker.getType().hasFlag(MiscType.F_ARTEMIS)) {
						dBV *= 1.2;
					}

					if ((mLinker.getType() instanceof MiscType) &&
							mLinker.getType().hasFlag(MiscType.F_APOLLO)) {
						dBV *= 1.15;
					}
				}

				// half for being rear mounted (or front mounted, when more
				// rear-than front-mounted un-modded BV
				if (((weapon.isRearMounted() || (weapon.getLocation() == LOC_AFT)) && halveRear) ||
						(!(weapon.isRearMounted() || (weapon.getLocation() == LOC_AFT)) && !halveRear)) {
					dBV /= 2;
				}
				int heat = ((WeaponType) weapon.getType()).getHeat();

				// double heat for ultras
				if ((wtype.getAmmoType() == AmmoType.T_AC_ULTRA) ||
						(wtype.getAmmoType() == AmmoType.T_AC_ULTRA_THB)) {
					heat *= 2;
				}

				// Six times heat for RAC
				if (wtype.getAmmoType() == AmmoType.T_AC_ROTARY) {
					heat *= 6;
				}

				double[] weaponValues = new double[2];
				weaponValues[0] = dBV;
				weaponValues[1] = heat;

				if (heat > 0) {
					// store heat and BV, for sorting a few lines down
					weaponValues[0] = dBV;
					weaponValues[1] = heat;
					heatBVs.add(weaponValues);
				}
				else {
					nonHeatBVs.add(dBV);
				}
			}
			// sort the heat-using weapons by modified BV
			Collections.sort(heatBVs, new Comparator<double[]>() {
				public int compare(double[] obj1, double[] obj2)
				{
					// if same BV, lower heat first
					if (obj1[0] == obj2[0]) {
						return (int) Math.ceil(obj1[1] - obj2[1]);
					}

					// higher BV first
					return (int) Math.ceil(obj2[0] - obj1[0]);
				}
			});

			// count heat-free weapons at full modified BV
			for (double bv : nonHeatBVs) {
				weaponBV += bv;
			}

			// count heat-generating weapons at full modified BV until
			// heatefficiency is reached or passed with one weapon
			double heatAdded = 0;
			for (double[] weaponValues : heatBVs) {
				double dBV = weaponValues[0];
				if (heatAdded >= aeroHeatEfficiency) {
					dBV /= 2;
				}
				heatAdded += weaponValues[1];
				weaponBV += dBV;
			}
		}

		// add offensive misc. equipment BV
		double oEquipmentBV = 0;
		for (Mounted mounted : getMisc()) {
			MiscType mtype = (MiscType) mounted.getType();

			// don't count destroyed equipment
			if (mounted.isDestroyed()) {
				continue;
			}

			if (mtype.hasFlag(MiscType.F_TARGCOMP)) {
				continue;
			}

			double bv = mtype.getBattleValue(this);
			oEquipmentBV += bv;
		}
		weaponBV += oEquipmentBV;

		// add ammo battleValue
		double ammoBV = 0;
		// extra BV for when we have semiguided LRMs and someone else has TAG
		// on our team
		double tagBV = 0;
		Map<String, Double> ammo = new HashMap<String, Double>();
		ArrayList<String> keys = new ArrayList<String>();

		for (Mounted mounted : getAmmo()) {
			AmmoType atype = (AmmoType) mounted.getType();

			// don't count depleted ammo
			if (mounted.getShotsLeft() == 0) {
				continue;
			}

			// don't count AMS, it's defensive
			if (atype.getAmmoType() == AmmoType.T_AMS) {
				continue;
			}
			// don't count screen launchers, they are defensive
			if (atype.getAmmoType() == AmmoType.T_SCREEN_LAUNCHER) {
				continue;
			}

			// don't count oneshot ammo, it's considered part of the launcher.
			if (mounted.getLocation() == Entity.LOC_NONE) {
				// assumption: ammo without a location is for a oneshot weapon
				continue;
			}

			// semiguided or homing ammo might count double
			if ((atype.getMunitionType() == AmmoType.M_SEMIGUIDED) ||
					(atype.getMunitionType() == AmmoType.M_HOMING)) {
				Player tmpP = getOwner();

				if (tmpP != null) {
					// Okay, actually check for friendly TAG.
					if (tmpP.hasTAG()) {
						tagBV += atype.getBattleValue(this);
					}
					else if ((tmpP.getTeam() != Player.TEAM_NONE) &&
							(game != null)) {
						for (Enumeration<Team> e = game.getTeams(); e
								.hasMoreElements();) {
							Team team = e.nextElement();
							if (team.getId() == tmpP.getTeam()) {
								if (team.hasTAG(game)) {
									tagBV += atype.getBattleValue(this);
								}
								// A player can't be on two teams.
								// If we check his team and don't give the
								// penalty, that's it.
								break;
							}
						}
					}
				}
			}

			String key = atype.getAmmoType() + ":" + atype.getRackSize();
			if (!keys.contains(key)) {
				keys.add(key);
			}

			if (!ammo.containsKey(key)) {
				ammo.put(key, atype.getBattleValue(this));
			}
			else {
				ammo.put(key, atype.getBattleValue(this) + ammo.get(key));
			}
		}

		// Excessive ammo rule:
		// Only count BV for ammo for a weapontype until the BV of all weapons
		// of that type on the mech is reached.
		for (String key : keys) {
			if (weaponsForExcessiveAmmo.get(key) != null) {
				if (ammo.get(key) > weaponsForExcessiveAmmo.get(key)) {
					ammoBV += weaponsForExcessiveAmmo.get(key);
				}
				else {
					ammoBV += ammo.get(key);
				}
			}
		}
		weaponBV += ammoBV;

		// adjust further for speed factor
		double speedFactor = Math
				.pow(1 + (((double) getRunMP() - 5) / 10), 1.2);
		speedFactor = Math.round(speedFactor * 100) / 100.0;

		obv = weaponBV * speedFactor;
		double finalBV = dbv + obv;

		if (getCockpitType() == Aero.COCKPIT_SMALL) {
			finalBV *= 0.95;
		}
		finalBV = Math.round(finalBV);

		// we get extra battleValue from some stuff
		double xbv = 0.0;
		// extra BV for semi-guided lrm when TAG in our team
		xbv += tagBV;
		// extra from c3 networks. a valid network requires at least 2 members
		// some hackery and magic numbers here. could be better also, each
		// 'has' loops through all equipment. inefficient to do it 3 times
		if (((hasC3MM() && (calculateFreeC3MNodes() < 2)) ||
				(hasC3M() && (calculateFreeC3Nodes() < 3)) ||
				(hasC3S() && (c3Master > NONE)) || (hasC3i() && (calculateFreeC3Nodes() < 5))) &&
				!ignoreC3 && (game != null)) {
			int totalForceBV = 0;
			totalForceBV += this.calculateBattleValue(true, true);

			for (Entity e : game.getC3NetworkMembers(this)) {
				if (!equals(e) && onSameC3NetworkAs(e)) {
					totalForceBV += e.calculateBattleValue(true, true);
				}
			}
			xbv += totalForceBV *= 0.05;
		}
		finalBV += xbv;

		// and then factor in pilot
		double pilotFactor = 1;

		if (!ignorePilot) {
			pilotFactor = crew.getBVSkillMultiplier();
		}

		int retVal = (int) Math.round((finalBV) * pilotFactor);

		// don't factor pilot in if we are just calculating BV for C3 extra BV
		if (ignoreC3) {
			return (int) finalBV;
		}

		return retVal;
	}



	/**
	 * Gets the bV type modifier.
	 * 
	 * @return the bV type modifier.
	 */
	public double getBVTypeModifier()
	{
		return 1.2;
	}



	/**
	 * Add in piloting skill mods. Gets the number of avionic hits and pilot
	 * hits and stores those values which are used in a series of comparisons
	 * which determine what gets add to prd.
	 * 
	 * @see megamek.common.Entity#addEntityBonuses(megamek.common.PilotingRollData)
	 */
	@Override
	public PilotingRollData addEntityBonuses(PilotingRollData prd)
	{
		// this is a control roll. Affected by: avionics damage pilot damage
		// current velocity
		int avihits = getAvionicsHits();
		int pilothits = getCrew().getHits();

		if ((avihits > 0) && (avihits < 3)) {
			prd.addModifier(avihits, "Avionics Damage");
		}

		// this should probably be replaced with some kind of AVI_DESTROYED
		// boolean
		if (avihits >= 3) {
			prd.addModifier(5, "Avionics Destroyed");
		}

		if (pilothits > 0) {
			prd.addModifier(pilothits, "Pilot Hits");
		}

		// movement effects some question as to whether "above safe thrust"
		// applies to thrust or velocity I will treat it as thrust until it
		// is resolved
		if (moved == IEntityMovementType.MOVE_OVER_THRUST) {
			prd.addModifier(+1, "Used more than safe thrust");
		}

		int vel = getCurrentVelocity();
		int vmod = vel - (2 * getWalkMP());

		if (vmod > 0) {
			prd.addModifier(vmod, "Velocity greater than 2x safe thrust");
		}

		// add in atmospheric effects later
		if (game.getBoard().inAtmosphere()) {
			prd.addModifier(+2, "Atmospheric operations");

			// check type
			if (this instanceof Dropship) {
				if (isSpheroid()) {
					prd.addModifier(-1, "spheroid dropship");
				}
				else {
					prd.addModifier(0, "aerodyne dropship");
				}
			}
			else {
				prd.addModifier(-1, "fighter/small craft");
			}
		}

		// life support (only applicable to non-ASFs
		if (!hasLifeSupport()) {
			prd.addModifier(+2, "No life support");
		}

		if (hasModularArmor()) {
			prd.addModifier(1, "Modular Armor");
		}

		// VDNI bonus?
		if (getCrew().getOptions().booleanOption("vdni") &&
				!getCrew().getOptions().booleanOption("bvdni")) {
			prd.addModifier(-1, "VDNI");
		}

		// Small/torso-mounted cockpit penalty?
		if ((getCockpitType() == Aero.COCKPIT_SMALL) &&
				!getCrew().getOptions().booleanOption("bvdni")) {
			prd.addModifier(1, "Small Cockpit");
		}

		return prd;
	}



	/**
	 * Generates a vector containing reports on all useful information about
	 * this entity.
	 * 
	 * @see megamek.common.Entity#victoryReport()
	 */
	@Override
	public Vector<Report> victoryReport()
	{
		Vector<Report> vDesc = new Vector<Report>();

		Report report = new Report(7025);
		report.type = Report.PUBLIC;
		report.addDesc(this);
		vDesc.addElement(report);

		report = new Report(7035);
		report.type = Report.PUBLIC;
		report.newlines = 0;
		vDesc.addElement(report);
		vDesc.addAll(crew.getDescVector(false));
		report = new Report(7070, Report.PUBLIC);
		report.add(getKillNumber());
		vDesc.addElement(report);

		if (isDestroyed()) {
			Entity killer = game.getEntity(killerId);
			if (killer == null) {
				killer = game.getOutOfGameEntity(killerId);
			}

			if (killer != null) {
				report = new Report(7072, Report.PUBLIC);
				report.addDesc(killer);
			}
			else {
				report = new Report(7073, Report.PUBLIC);
			}

			vDesc.addElement(report);
		}
		report.newlines = 2;

		return vDesc;
	}



	/**
	 * Returns the number of slots.
	 * 
	 * @see megamek.common.Entity#getNoOfSlots()
	 */
	@Override
	public int[] getNoOfSlots()
	{
		return NUM_OF_SLOTS.clone();
	}



	/**
	 * Returns this entity's running or flank mp modified for heat and gravity.
	 * Note: Tanks don't have MASC.
	 * 
	 * @param gravity the gravity of this entity.
	 * @param ignoreheat the ignore heat.
	 * @return the running or flank mp.
	 */
	@Override
	public int getRunMPwithoutMASC(boolean gravity, boolean ignoreheat)
	{
		return getRunMP(gravity, ignoreheat);
	}



	/**
	 * Returns the capacity the heat can sink each turn.
	 * 
	 * @see megamek.common.Entity#getHeatCapacity()
	 */
	@Override
	public int getHeatCapacity()
	{
		return (getHeatSinks() * (getHeatType() + 1));
	}



	/**
	 * Returns the capacity the heat can sink each turn if it is in water.
	 * 
	 * @see megamek.common.Entity#getHeatCapacityWithWater()
	 */
	@Override
	public int getHeatCapacityWithWater()
	{
		return getHeatCapacity();
	}



	/**
	 * Returns heat generated by engine crits.
	 * 
	 * @see megamek.common.Entity#getEngineCritHeat()
	 */
	@Override
	public int getEngineCritHeat()
	{
		return 0;
	}



	/**
	 * Automatically set the internal structure of this entity.
	 * 
	 * @see megamek.common.Entity#autoSetInternal()
	 */
	@Override
	public void autoSetInternal()
	{
		// should be no internals because only one SI
		// It doesn't seem to be screwing anything up yet.
		// Need to figure out how destruction of entity is determined
		int nInternal = (int) Math.ceil(weight / 10.0);
		nInternal = 0;
		// I need to look at safe thrust as well at some point

		for (int x = 0; x < locations(); x++) {
			initializeInternal(nInternal, x);
		}
	}



	/**
	 * Automatically set the threshold.
	 */
	public void autoSetThresh()
	{
		for (int x = 0; x < locations(); x++) {
			initializeThresh(x);
		}
	}



	/**
	 * Sets the threshold.
	 * 
	 * @param val the value of the threshold.
	 * @param loc the index of the threshold in the array.
	 */
	public void setThresh(int val, int loc)
	{
		damThresh[loc] = val;
	}



	/**
	 * Initializes the threshold.
	 * 
	 * @param loc the index of the threshold.
	 */
	public void initializeThresh(int loc)
	{
		int nThresh = (int) Math.ceil(getArmor(loc) / 10.0);
		setThresh(nThresh, loc);
	}



	/**
	 * Gets the threshold.
	 * 
	 * @param loc the index of the threshold in the array.
	 * @return the threshold.
	 */
	public int getThresh(int loc)
	{
		return damThresh[loc];
	}



	/**
	 * Determines whether or not the unit can be repaired or only harvested for
	 * spares.
	 * 
	 * @return true if the unit can be repaired (given enough time and parts).
	 * @see Entity#isSalvage()
	 */
	@Override
	public boolean isRepairable()
	{
		return true; // deal with this later
	}



	/**
	 * Determines whether or not this entity can perform charges.
	 * 
	 * @see megamek.common.Entity#canCharge()
	 */
	@Override
	public boolean canCharge()
	{
		// ramming is resolved differently than charging
		return false;
	}



	/**
	 * Determines whether or not this entity can perform DFA attacks. Returns
	 * false since an Aero can't perform this type of attack.
	 * 
	 * @see megamek.common.Entity#canDFA()
	 */
	@Override
	public boolean canDFA()
	{
		return false;
	}



	/**
	 * Determines whether or not this Aero can perform ram charges.
	 * 
	 * @see megamek.common.Entity#canRam()
	 */
	@Override
	public boolean canRam()
	{
		return !isImmobile() && (getWalkMP() > 0);
	}



	/**
	 * Returns the armor type.
	 * 
	 * @see megamek.common.Entity#getArmorType()
	 */
	@Override
	public int getArmorType()
	{
		return armorType;
	}



	/**
	 * Sets the armor type.
	 * 
	 * @see megamek.common.Entity#setArmorType(int)
	 */
	@Override
	public void setArmorType(int type)
	{
		armorType = type;
	}



	/**
	 * Returns the structure type.
	 * 
	 * @see megamek.common.Entity#getStructureType()
	 */
	@Override
	public int getStructureType()
	{
		return structureType;
	}



	/**
	 * Sets the structure type.
	 * 
	 * @see megamek.common.Entity#setStructureType(int)
	 */
	@Override
	public void setStructureType(int type)
	{
		structureType = type;
	}



	/**
	 * Gets the suspension factor.
	 * 
	 * @return suspension factor of vehicle.
	 */
	// Doesn't really do anything so just return 0
	public int getSuspensionFactor()
	{
		return 0;
	}



	/*
	 * There is a mistake in some of the AT2r costs for some reason they added
	 * ammo twice for a lot of the level 2 designs, leading to costs that are
	 * too high
	 */
	/**
	 * Returns the cost of the entity.
	 * 
	 * @see megamek.common.Entity#getCost()
	 */
	@Override
	public double getCost()
	{
		double cost = 0;

		// add in cockpit
		cost += 200000 + 50000 + 2000 * weight;

		// Structural integrity
		cost += 50000 * getStructIntegtiy();

		// additional flight systems (attitude thruster and landing gear)
		cost += 25000 + 10 * getWeight();

		// engine
		Engine engine = getEngine();
		cost += engine.getBaseCost() * engine.getRating() * weight / 75.0;

		// fuel tanks
		cost += 200 * getFuel() / 80.0;

		// armor
		cost += getArmorWeight() * EquipmentType.getArmorCost(armorType);

		// heat sinks
		int sinkCost = 2000 + 4000 * getHeatType();// == HEAT_DOUBLE ? 6000:
													// 2000;
		cost += sinkCost * getHeatSinks();

		// weapons
		cost += getWeaponsAndEquipmentCost();

		// omni multiplier
		double omniMultiplier = 1;
		if (isOmni()) {
			omniMultiplier = 1.25f;
		}

		double weightMultiplier = 1 + (weight / 200f);

		return Math.round(cost * omniMultiplier * weightMultiplier);
	}



	/**
	 * Determines whether or not this entity is doomed in vacuum.
	 * 
	 * @see megamek.common.Entity#doomedInVacuum()
	 */
	@Override
	public boolean doomedInVacuum()
	{
		return false;
	}



	/**
	 * Determines whether or not this entity is doomed on the ground.
	 * 
	 * @see megamek.common.Entity#doomedOnGround()
	 */
	@Override
	public boolean doomedOnGround()
	{
		return true;
	}



	/**
	 * Determines whether or not this entity is doomed in the air.
	 * 
	 * @see megamek.common.Entity#doomedInAtmosphere()
	 */
	@Override
	public boolean doomedInAtmosphere()
	{
		return false;
	}



	/**
	 * Determines whether or not this entity is doomed in space.
	 * 
	 * @see megamek.common.Entity#doomedInSpace()
	 */
	@Override
	public boolean doomedInSpace()
	{
		return false;
	}



	/**
	 * Determines whether or not this entity can go hull down.
	 * 
	 * @see megamek.common.Entity#canGoHullDown()
	 */
	@Override
	public boolean canGoHullDown()
	{
		return false;
	}



	/*
	 * public void addMovementDamage(int level) { movementDamage += level; }
	 */

	/**
	 * Sets the engine.
	 * 
	 * @param engine the new engine.
	 */
	public void setEngine(Engine engine)
	{
		this.engine = engine;

		if (engine.engineValid) {
			setOriginalWalkMP(calculateWalk());
		}
	}



	/**
	 * Returns the percent of the structure integrity remaining.
	 * 
	 * @return the internal remaining percent.
	 */
	@Override
	public double getInternalRemainingPercent()
	{
		return ((double) getStructIntegtiy() / (double) getOrigStructIntegtiy());
	}



	/**
	 * Calculate walk.
	 * 
	 * @return the walk value.
	 */
	protected int calculateWalk()
	{
		return (getEngine().getRating() / (int) weight) + 2;
	}



	/**
	 * Checks if the unit is hardened.
	 * 
	 * @see megamek.common.Entity#isNuclearHardened()
	 */
	@Override
	public boolean isNuclearHardened()
	{
		return true;
	}



	/**
	 * Adds a piece of equipment.
	 * 
	 * @see megamek.common.Entity#addEquipment(megamek.common.Mounted, int,
	 *      boolean)
	 */
	@Override
	protected void addEquipment(Mounted mounted, int loc, boolean rearMounted)
			throws LocationFullException
	{
		super.addEquipment(mounted, loc, rearMounted);
		// Add the piece equipment to our slots.
		addCritical(loc, new CriticalSlot(CriticalSlot.TYPE_EQUIPMENT,
				getEquipmentNum(mounted), true, mounted));
	}



	/**
	 * Gets the type of critical effect caused by a critical roll, taking
	 * account of existing damage.
	 * 
	 * @param roll the final dice roll.
	 * @param target the target.
	 * @return a critical type.
	 */
	public int getCriticalEffect(int roll, int target)
	{
		// just grab the latest potential crit
		if (roll < target) {
			return CRIT_NONE;
		}

		return getPotCrit();
	}



	/**
	 * Check a thrust against the structIntegrity. First calls the
	 * <code>getBasePilotingRoll</code> method and checks if the thrust is
	 * greater than the <code>structIntegrity</code>. If it is, append a new
	 * <code>PilotingRollData</code> to the roll if it is. Else it adds a
	 * modifier to the roll.
	 * 
	 * @param thrust the thrust to check.
	 * @param overallMoveType the overall movement type.
	 * @return the piloting roll data.
	 */
	public PilotingRollData checkThrustSI(int thrust, int overallMoveType)
	{
		PilotingRollData roll = getBasePilotingRoll(overallMoveType);

		if (thrust > getStructIntegtiy()) {
			// append the reason modifier
			roll.append(new PilotingRollData(getId(), thrust -
					getStructIntegtiy(),
					"Thrust exceeds current SI in a single hex"));
		}
		else {
			roll.addModifier(TargetRoll.CHECK_FALSE,
					"Check false: Entity is not exceeding SI");
		}

		return roll;
	}



	/**
	 * Check a thrust against the structIntegrity. First calls the
	 * <code>getBasePilotingRoll</code> method and checks if the thrust is
	 * greater than the <code>structIntegrity</code>. If it is, append a new
	 * <code>PilotingRollData</code> to the roll if it is. Else it adds a
	 * modifier to the roll.
	 * 
	 * @param thrust the thrust.
	 * @param overallMoveType the overall move type.
	 * @return the piloting roll data.
	 */
	public PilotingRollData checkThrustSITotal(int thrust, int overallMoveType)
	{
		PilotingRollData roll = getBasePilotingRoll(overallMoveType);

		if (thrust > getStructIntegtiy()) {
			// append the reason modifier
			roll.append(new PilotingRollData(getId(), 0,
					"Thrust spent this turn exceeds current SI"));
		}
		else {
			roll.addModifier(TargetRoll.CHECK_FALSE,
					"Check false: Entity is not exceeding SI");
		}

		return roll;
	}



	/**
	 * Checks velocity double. It calls <code>getBasePilotingRoll</code> method
	 * and checks if the velocity is greater than the value returned by a call
	 * to the <code>getWalkMP</code> method time 2.
	 * 
	 * @param velocity the velocity.
	 * @param overallMoveType the overall move type.
	 * @return the piloting roll data.
	 */
	public PilotingRollData checkVelocityDouble(int velocity,
			int overallMoveType)
	{
		PilotingRollData roll = getBasePilotingRoll(overallMoveType);

		if ((velocity > (2 * getWalkMP())) && game.getBoard().inAtmosphere()) {
			// append the reason modifier
			roll.append(new PilotingRollData(getId(), 0,
					"Velocity greater than 2x safe thrust"));
		}
		else {
			roll.addModifier(TargetRoll.CHECK_FALSE,
					"Check false: Entity is not exceeding 2x safe thrust");
		}

		return roll;
	}



	/**
	 * Checks down. Calls the <code>getBasePilotingRoll</code> method. Then
	 * checks whether the drop value is greater than two and either adds a
	 * modifier or appends a new <code>PilotingRollData</code> to the roll.
	 * 
	 * @param drop the drop value.
	 * @param overallMoveType the overall move type.
	 * @return the piloting roll data.
	 */
	public PilotingRollData checkDown(int drop, int overallMoveType)
	{
		PilotingRollData roll = getBasePilotingRoll(overallMoveType);

		if (drop > 2) {
			// append the reason modifier
			roll.append(new PilotingRollData(getId(), drop,
					"lost more than two altitudes"));
		}
		else {
			roll.addModifier(TargetRoll.CHECK_FALSE,
					"Check false: entity did not drop more than two altitudes");
		}

		return roll;
	}



	/**
	 * Checks hover. Calls the <code>getBasePilotingRoll</code> method and
	 * determines if the move path entered contains the STEP_HOVER.
	 * 
	 * @param path the move path.
	 * @return the piloting roll data.
	 */
	public PilotingRollData checkHover(MovePath path)
	{
		PilotingRollData roll = getBasePilotingRoll(path
				.getLastStepMovementType());

		if (path.contains(MovePath.STEP_HOVER)) {
			// append the reason modifier
			roll.append(new PilotingRollData(getId(), 0, "hovering"));
		}
		else {
			roll.addModifier(TargetRoll.CHECK_FALSE,
					"Check false: entity did not hover");
		}

		return roll;
	}



	/**
	 * Checks stall. Calls the <code>getBasePilotingRoll</code> method and
	 * determines whether or not the velocity is equal to zero.
	 * 
	 * @param velocity the velocity.
	 * @param overallMoveType the overall move type.
	 * @return the piloting roll data.
	 */
	public PilotingRollData checkStall(int velocity, int overallMoveType)
	{
		PilotingRollData roll = getBasePilotingRoll(overallMoveType);

		if (velocity == 0) {
			// append the reason modifier
			roll.append(new PilotingRollData(getId(), 0, "stalled out"));
		}
		else {
			roll.addModifier(TargetRoll.CHECK_FALSE,
					"Check false: entity not stalled out");
		}

		return roll;
	}



	/**
	 * Checks rolls. Calls the <code>getBasePilotingRoll</code> method and
	 * checks if the MoveStep entered is of type STEP_ROLL or STEP_YAW and the
	 * number of rolls is greater than one.
	 * 
	 * @param step the step to move.
	 * @param overallMoveType the overall move type.
	 * @return the piloting roll data.
	 */
	public PilotingRollData checkRolls(MoveStep step, int overallMoveType)
	{
		PilotingRollData roll = getBasePilotingRoll(overallMoveType);

		if (((step.getType() == MovePath.STEP_ROLL) || (step.getType() == MovePath.STEP_YAW)) &&
				(step.getNRolls() > 1)) {
			// append the reason modifier
			roll.append(new PilotingRollData(getId(), 0,
					"More than one roll in the same turn"));
		}
		else {
			roll.addModifier(TargetRoll.CHECK_FALSE,
					"Check false: Entity is not rolling more than once");
		}

		return roll;
	}



	/**
	 * Checks if a maneuver requires a control roll. Calls the
	 * <code>getBasePilotingRoll</code> method and checks if the step is equal
	 * to null or the type is not STEP_MANEUVER.
	 * 
	 * @param step the step.
	 * @param overallMoveType the overall move type.
	 * @return the piloting roll data.
	 */
	public PilotingRollData checkManeuver(MoveStep step, int overallMoveType)
	{
		PilotingRollData roll = getBasePilotingRoll(overallMoveType);

		if ((step == null) || (step.getType() != MovePath.STEP_MANEUVER)) {
			roll.addModifier(TargetRoll.CHECK_FALSE,
					"Check false: Entity is not attempting to get up.");
			return roll;
		}

		roll.append(new PilotingRollData(getId(), ManeuverType.getMod(
				step.getManeuverType(), isVSTOL()), ManeuverType
				.getTypeName(step.getManeuverType()) + " maneuver"));

		return roll;
	}



	/**
	 * Adds a clan case in every location.
	 */
	public void addClanCase()
	{
		boolean explosiveFound = false;
		EquipmentType clCase = EquipmentType.get("CLCASE");

		for (int i = 0; i < locations(); i++) {
			explosiveFound = false;
			for (Mounted m : getEquipment()) {
				if (m.getType().isExplosive() && (m.getLocation() == i)) {
					explosiveFound = true;
				}
			}

			if (explosiveFound) {
				try {
					addEquipment(new Mounted(this, clCase), i, false);
				}
				catch (LocationFullException ex) {
					// um, that's impossible.
				}
			}
		}
	}



	/**
	 * check to see if case is available anywhere.
	 * 
	 * @return true if the case is available anywhere.
	 */
	public boolean hasCase()
	{

		boolean hasCase = false;

		for (int x = 0; x < locations(); x++) {
			if (!hasCase) {
				hasCase = locationHasCase(x);
			}
		}

		return hasCase;
	}



	/**
	 * Used to determine net velocity of ramming attack.
	 * 
	 * @param src the source coordinates
	 * @return the net velocity of a ramming attack.
	 */
	public int sideTableRam(Coords src)
	{
		int side = sideTableRam(src, facing);
		// if using advanced movement, then I need heading, but
		// in cases of ties, I use the least damaging option
		if (game.useVectorMove()) {
			int newside = chooseSideRam(src);
			if (newside != -1) {
				side = newside;
			}
		}

		return side;
	}



	/**
	 * Used to determine net velocity of ramming attack.
	 * 
	 * @param src the source coordinates
	 * @param face the face.
	 * @return the net velocity of ramming attacks
	 */
	public int sideTableRam(Coords src, int face)
	{
		int fa = (getPosition().degree(src) + (6 - face) * 60) % 360;

		if (((fa > 30) && (fa <= 90)) || ((fa < 330) && (fa >= 270))) {
			return Aero.RAM_TOWARD_OBL;
		}
		else if ((fa > 150) && (fa < 210)) {
			return Aero.RAM_AWAY_DIR;
		}
		else if (((fa > 90) && (fa <= 150)) || ((fa < 270) && (fa >= 210))) {
			return Aero.RAM_AWAY_OBL;
		}
		else {
			return Aero.RAM_TOWARD_DIR;
		}
	}



	/**
	 * Chooses a side ram.
	 * 
	 * @param src the source coordinates
	 * @return the side ram.
	 */
	public int chooseSideRam(Coords src)
	{
		// loop through directions and if we have a non-zero vector, then
		// compute the targetsidetable. If we come to a higher vector, then
		// replace. If we come to an equal vector then take it if it is better
		int thrust = 0;
		int high = -1;
		int side = -1;

		for (int dir = 0; dir < 6; dir++) {
			thrust = getVector(dir);
			if (thrust == 0) {
				continue;
			}

			if (thrust > high) {
				high = thrust;
				side = sideTableRam(src, dir);
			}

			// what if they tie
			if (thrust == high) {
				int newside = sideTableRam(src, dir);
				// choose the better
				if (newside > side) {
					newside = side;
				}
				// that should be the only case, because it can't shift you
				// from front to aft or vice-versa
			}

		}

		return side;
	}



	/**
	 * Gets the standard damage.
	 * 
	 * @param loc the location.
	 * @return the standard damage.
	 */
	public int getStandardDamage(int loc)
	{
		return standard_damage[loc];
	}



	/**
	 * Reset the standard damage.
	 */
	public void resetStandardDamage()
	{
		for (int i = 0; i < locations(); i++) {
			standard_damage[i] = 0;
		}
	}



	/**
	 * Adds the standard damage.
	 * 
	 * @param damage the damage.
	 * @param hit the hit.
	 */
	public void addStandardDamage(int damage, HitData hit)
	{
		standard_damage[hit.getLocation()] = standard_damage[hit.getLocation()] +
				damage;
	}



	/**
	 * Gets the maximum engine hits.
	 * 
	 * @return the maximum engine hits.
	 */
	public int getMaxEngineHits()
	{
		return 3;
	}



	/**
	 * Returns the maximum elevation change.
	 * 
	 * @see megamek.common.Entity#getMaxElevationChange()
	 */
	@Override
	public int getMaxElevationChange()
	{
		return 999;
	}



	/**
	 * Determines whether or not the hex has a prohibited terrain.
	 * 
	 * @see megamek.common.Entity#isHexProhibited(megamek.common.IHex)
	 */
	@Override
	public boolean isHexProhibited(IHex hex)
	{
		if (hex.containsTerrain(Terrains.IMPASSABLE)) {
			return true;
		}

		return false;
	}



	/**
	 * Checks if is spheroid.
	 * 
	 * @return true if is spheroid.
	 */
	public boolean isSpheroid()
	{
		return spheroid;
	}



	/**
	 * Sets the spheroid.
	 * 
	 * @param bool the spheroid to set.
	 */
	public void setSpheroid(boolean bool)
	{
		spheroid = bool;
	}



	/**
	 * Returns the height.
	 * 
	 * @see megamek.common.Entity#height()
	 */
	@Override
	public int height()
	{
		return 0;
	}



	/**
	 * Returns true if the entity has an RAC which is jammed and not destroyed.
	 * 
	 * @return true if successful.
	 */
	@Override
	public boolean canUnjamRAC()
	{
		for (Mounted mounted : getTotalWeaponList()) {
			WeaponType wtype = (WeaponType) mounted.getType();
			if ((wtype.getAmmoType() == AmmoType.T_AC_ROTARY) &&
					mounted.isJammed() && !mounted.isDestroyed()) {
				return true;
			}
		}

		return false;
	}



	// I need a function that takes the bombChoices variable and uses it to
	// produce bombs
	/**
	 * Apply bombs.
	 */
	public void applyBombs()
	{
		int loc = LOC_NOSE;

		for (int type = 0; type < BombType.B_NUM; type++) {
			for (int i = 0; i < bombChoices[type]; i++) {
				if ((type == BombType.B_ALAMO) &&
						!game.getOptions().booleanOption("at2_nukes")) {
					continue;
				}

				if ((type > BombType.B_TAG) &&
						!game.getOptions().booleanOption("allow_advanced_ammo")) {
					continue;
				}

				// some bombs need an associated weapon and if so
				// they need a weapon for each bomb
				if ((null != BombType.getBombWeaponName(type)) &&
						(type != BombType.B_ARROW) &&
						(type != BombType.B_HOMING)) {
					try {
						addBomb(EquipmentType.get(BombType
								.getBombWeaponName(type)), loc);
					}
					catch (LocationFullException ex) {
						// throw new LocationFullException(ex.getMessage());
					}
				}

				if (type != BombType.B_TAG) {
					try {
						addEquipment(EquipmentType.get(BombType
								.getBombInternalName(type)), loc, false);
					}
					catch (LocationFullException ex) {
						// throw new LocationFullException(ex.getMessage());
					}
				}
			}
		}

		// add the space bomb attack
		// TODO: I don't know where else to put this (where do infantry attacks
		// get added)
		if (game.getOptions().booleanOption("stratops_space_bomb") &&
				(getSpaceBombs().size() > 0)) {
			try {
				addEquipment(EquipmentType.get(SPACE_BOMB_ATTACK), LOC_NOSE,
						false);
			}
			catch (LocationFullException ex) {
				// throw new LocationFullException(ex.getMessage());
			}
		}

		updateWeaponGroups();
		loadAllWeapons();
	}



	/**
	 * Gets the space bombs.
	 * 
	 * @return the space bombs.
	 */
	public Vector<Mounted> getSpaceBombs()
	{
		Vector<Mounted> bombs = new Vector<Mounted>();

		for (Mounted bomb : getBombs()) {
			BombType btype = (BombType) bomb.getType();
			if (!bomb.isInoperable() &&
					(bomb.getShotsLeft() > 0) &&
					((btype.getBombType() == BombType.B_HE) ||
							(btype.getBombType() == BombType.B_CLUSTER) ||
							(btype.getBombType() == BombType.B_LG) || (btype
							.getBombType() == BombType.B_ARROW))) {
				bombs.add(bomb);
			}
		}

		return bombs;
	}



	/**
	 * Returns the extreme range modifier.
	 * 
	 * @see megamek.common.Entity#getExtremeRangeModifier()
	 */
	@Override
	public int getExtremeRangeModifier()
	{
		return 6;
	}



	/**
	 * Gets the straight moves.
	 * 
	 * @return the straight moves.
	 */
	public int getStraightMoves()
	{
		return straightMoves;
	}



	/**
	 * Sets the straight moves.
	 * 
	 * @param moves the new straight moves.
	 */
	public void setStraightMoves(int moves)
	{
		straightMoves = moves;
	}



	/**
	 * Checks if is vSTOL.
	 * 
	 * @return true, if is vSTOL.
	 */
	public boolean isVSTOL()
	{
		return vstol;
	}



	/**
	 * Sets the vSTOL.
	 * 
	 * @param bool the vSTOL to set.
	 */
	public void setVSTOL(boolean bool)
	{
		vstol = bool;
	}



	/**
	 * Gets the amount of fuel used.
	 * 
	 * @param thrust the thrust.
	 * @return the amount of fuel used.
	 */
	public int getFuelUsed(int thrust)
	{
		int overThrust = Math.max(thrust - getWalkMP(), 0);
		int safeThrust = thrust - overThrust;
		return safeThrust + 2 * overThrust;
	}



	/**
	 * Determines whether or not it was a failed maneuver.
	 * 
	 * @return true if it was a failed maneuver.
	 */
	public boolean didFailManeuver()
	{
		return failedManeuver;
	}



	/**
	 * Sets the failed maneuver.
	 * 
	 * @param bool the new failed maneuver.
	 */
	public void setFailedManeuver(boolean bool)
	{
		failedManeuver = bool;
	}



	/**
	 * Sets the accDecNow.
	 * 
	 * @param bool the accDecNow to set.
	 */
	public void setAccDecNow(boolean bool)
	{
		accDecNow = bool;
	}



	/**
	 * Determines whether or not accDecNow.
	 * 
	 * @return true, if successful.
	 */
	public boolean didAccDecNow()
	{
		return accDecNow;
	}



	/**
	 * Sets the game options.
	 * 
	 * @see megamek.common.Entity#setGameOptions()
	 */
	@Override
	public void setGameOptions()
	{
		super.setGameOptions();

		for (Mounted mounted : getWeaponList()) {
			if ((mounted.getType() instanceof EnergyWeapon) &&
					(((WeaponType) mounted.getType()).getAmmoType() == AmmoType.T_NA) &&
					(game != null) &&
					game.getOptions().booleanOption("tacops_energy_weapons")) {

				ArrayList<String> modes = new ArrayList<String>();
				String[] stringArray = {};
				int damage = ((WeaponType) mounted.getType()).getDamage();

				if (damage == WeaponType.DAMAGE_VARIABLE) {
					damage = ((WeaponType) mounted.getType()).damageShort;
				}

				for (; damage >= 0; damage--) {
					modes.add("Damage " + damage);
				}

				if (((WeaponType) mounted.getType())
						.hasFlag(WeaponType.F_FLAMER)) {
					modes.add("Heat");
				}

				((WeaponType) mounted.getType()).setModes(modes
						.toArray(stringArray));
			}
		}
	}



	/**
	 * Determines whether or not this entity has modular armor by looping
	 * through all equipment. Returns true if the equipment wasn't destroyed, is
	 * of type MiscType and has flag F_MODULAR_ARMOR.
	 * 
	 * @see megamek.common.Entity#hasModularArmor()
	 */
	@Override
	public boolean hasModularArmor()
	{
		for (Mounted mount : this.getEquipment()) {
			if (!mount.isDestroyed() &&
					(mount.getType() instanceof MiscType) &&
					((MiscType) mount.getType())
							.hasFlag(MiscType.F_MODULAR_ARMOR)) {
				return true;
			}
		}

		return false;
	}



	/**
	 * Determines whether or not this entity has modular armor by looping
	 * through all equipment. Returns true if the equipment has a location equal
	 * to loc, is of type MiscType and has flag F_MODULAR_ARMOR.
	 * 
	 * @see megamek.common.Entity#hasModularArmor(int)
	 */
	@Override
	public boolean hasModularArmor(int loc)
	{
		for (Mounted mount : this.getEquipment()) {
			if ((mount.getLocation() == loc) &&
					(mount.getType() instanceof MiscType) &&
					((MiscType) mount.getType())
							.hasFlag(MiscType.F_MODULAR_ARMOR)) {
				return true;
			}
		}

		return false;
	}



	/**
	 * Returns the total tonnage of communications gear.
	 * 
	 * @see megamek.common.Entity#getTotalCommGearTons()
	 */
	@Override
	public int getTotalCommGearTons()
	{
		return 1 + getExtraCommGearTons();
	}



	/**
	 * The number of critical slots that are destroyed in the component.
	 * 
	 * @param type the type.
	 * @param index the index.
	 * @param loc the loc.
	 * @return the number of critical slots.
	 */
	@Override
	public int getBadCriticals(int type, int index, int loc)
	{
		return 0;
	}



	/**
	 * Gets the cockpit type.
	 * 
	 * @return the cockpit type.
	 */
	public int getCockpitType()
	{
		return cockpitType;
	}



	/**
	 * Sets the cockpit type.
	 * 
	 * @param type the new cockpit type.
	 */
	public void setCockpitType(int type)
	{
		cockpitType = type;
	}



	/**
	 * Gets the cockpit type.
	 * 
	 * @return the string representing the cockpit type.
	 */
	public String getCockpitTypeString()
	{
		return Aero.getCockpitTypeString(getCockpitType());
	}



	/**
	 * Gets the cockpit type.
	 * 
	 * @param inCockpitType the in cockpit type.
	 * @return the cockpit type string.
	 */
	public static String getCockpitTypeString(int inCockpitType)
	{
		if ((inCockpitType < 0) || (inCockpitType >= COCKPIT_STRING.length)) {
			return "Unknown";
		}

		return COCKPIT_STRING[inCockpitType];
	}



	/**
	 * Calculates the percent of armor remaining.
	 * 
	 * @see megamek.common.Entity#getArmorRemainingPercent()
	 */
	@Override
	public double getArmorRemainingPercent()
	{
		int armor0 = getTotalOArmor();
		int armor = getTotalArmor();

		if (isCapitalFighter()) {
			armor0 = getCapitalArmorOrig();
			armor = getCapitalArmor();
		}

		if (armor0 == 0) {
			return IArmorState.ARMOR_NA;
		}

		return ((double) armor / (double) armor0);
	}



	/**
	 * Determines whether or not the wings have suffered a weapon critical hit.
	 * 
	 * @return true if the wings have suffered a weapon critical hit.
	 */
	public boolean areWingsHit()
	{
		return wingsHit;
	}



	/**
	 * Sets the wings hit.
	 * 
	 * @param bool the new wings hit,
	 */
	public void setWingsHit(boolean bool)
	{
		wingsHit = bool;
	}



	/**
	 * Determines the opposite location to the one given.
	 * 
	 * @param loc the location.
	 * @return the opposite location.
	 */
	public int getOppositeLocation(int loc)
	{
		int opposite;

		switch (loc) {
		case Aero.LOC_NOSE:
			opposite = Aero.LOC_AFT;
			break;
		case Aero.LOC_LWING:
			opposite = Aero.LOC_RWING;
			break;
		case Aero.LOC_RWING:
			opposite = Aero.LOC_LWING;
			break;
		case Aero.LOC_AFT:
			opposite = Aero.LOC_NOSE;
			break;
		default:
			opposite = Aero.LOC_NOSE;
			break;
		}

		return opposite;
	}



	/**
	 * Returns the modifications to the cluster hit table for critical hits.
	 * 
	 * @return the modifications to the cluster hit table for critical hits.
	 */
	public int getClusterMods()
	{
		return -1 * (getFCSHits() + getSensorHits());
	}



	/**
	 * Determines the range of the ECM equipment.
	 * 
	 * @return the range of this unit's ECM. This value will be
	 *         <code>Entity.NONE</code> if no ECM is active.
	 */
	@Override
	public int getECMRange()
	{
		if (!game.getOptions().booleanOption("stratops_ecm") ||
				!game.getBoard().inSpace()) {
			return super.getECMRange();
		}

		return Math.min(super.getECMRange(), 0);
	}



	/**
	 * Determines the ECCM strength.
	 * 
	 * @return the strength of the ECCM field this unit emits,
	 */
	@Override
	public double getECCMStrength()
	{
		if (!game.getOptions().booleanOption("stratops_ecm") ||
				!game.getBoard().inSpace()) {
			return super.getECCMStrength();
		}

		if (hasActiveECCM()) {
			return 1;
		}

		return 0;
	}



	/**
	 * Sets the ECCM roll.
	 * 
	 * @param roll the value to set as the ECCM roll.
	 */
	public void setECCMRoll(int roll)
	{
		eccmRoll = roll;
	}



	/**
	 * Gets the ECCM roll.
	 * 
	 * @return the ECCM roll.
	 */
	public int getECCMRoll()
	{
		return eccmRoll;
	}



	/**
	 * Gets the ECCM target.
	 * 
	 * @return the ECCM target.
	 */
	public int getECCMTarget()
	{
		return crew.getPiloting() + getSensorHits() + getCICHits() +
				getFCSHits();
	}



	/**
	 * Gets the ECCM bonus.
	 * 
	 * @return the ECCM bonus.
	 */
	public int getECCMBonus()
	{
		return Math.max(0, eccmRoll - getECCMTarget());
	}



	/**
	 * Checks if the crew of this vessel is protected gravitational effects.
	 * 
	 * @return true if the crew of this vessel protected from gravitational
	 *         effects, see StratOps, pg. 36
	 */
	public boolean isCrewProtected()
	{
		return true;
	}



	/**
	 * Gets the gravity secondary threshold.
	 * 
	 * @return the gravity secondary threshold.
	 */
	public int getGravSecondaryThreshold()
	{
		int thresh = 6;

		if (isCrewProtected()) {
			thresh = 12;
		}

		// TODO: clan phenotypes
		return thresh;
	}



	/**
	 * Gets the gravity primary threshold.
	 * 
	 * @return the gravity primary threshold.
	 */
	public int getGravPrimaryThreshold()
	{
		int thresh = 12;

		if (isCrewProtected()) {
			thresh = 22;
		}

		// TODO: clan phenotypes
		return thresh;
	}



	/**
	 * Determines if this object can accept the given unit. The unit may not be
	 * of the appropriate type or there may be no room for the unit.
	 * 
	 * @param unit - the <code>Entity</code> to be loaded.
	 * @return <code>true</code> if the unit can be loaded, <code>false</code>
	 *         otherwise.
	 */
	@Override
	public boolean canLoad(Entity unit)
	{
		// capital fighters can load other capital fighters
		// (becoming squadrons) but not in the deployment phase
		if (isCapitalFighter() && !unit.isEnemyOf(this) &&
				unit.isCapitalFighter() && (getId() != unit.getId()) &&
				(game.getPhase() != IGame.Phase.PHASE_DEPLOYMENT)) {
			return true;
		}

		return super.canLoad(unit);
	}



	/**
	 * Uses the specified amount of fuel for this Aero. The amount may be
	 * adjusted by certain game options.
	 * 
	 * @param fuel the fuel to be used.
	 */
	public void useFuel(int fuel)
	{
		setFuel(Math.max(0, getFuel() - fuel));
	}



	/**
	 * Updates the weapon groups.
	 */
	public void updateWeaponGroups()
	{
		// first we need to reset all the weapons in our existing mounts to
		// zero until proven otherwise
		Set<String> set = weaponGroups.keySet();
		Iterator<String> iter = set.iterator();

		while (iter.hasNext()) {
			String key = iter.next();
			this.getEquipment(weaponGroups.get(key)).setNWeapons(0);
		}

		// now collect a hash of all the same weapons in each location by id
		Map<String, Integer> groups = new HashMap<String, Integer>();

		for (Mounted mounted : getTotalWeaponList()) {
			int loc = mounted.getLocation();
			if ((loc == Aero.LOC_RWING) || (loc == Aero.LOC_LWING)) {
				loc = Aero.LOC_WINGS;
			}

			if (mounted.isRearMounted()) {
				loc = Aero.LOC_AFT;
			}

			String key = mounted.getType().getInternalName() + ":" + loc;
			if (null == groups.get(key)) {
				groups.put(key, mounted.getNWeapons());
			}
			else {
				groups.put(key, groups.get(key) + mounted.getNWeapons());
			}
		}
		// now we just need to traverse the hash and either update our existing
		// equipment or add new ones if there is none
		Set<String> newSet = groups.keySet();
		Iterator<String> newIter = newSet.iterator();

		while (newIter.hasNext()) {
			String key = newIter.next();
			if (null != weaponGroups.get(key)) {
				// then this equipment is already loaded, so we just need to
				// correctly update the number of weapons
				this.getEquipment(weaponGroups.get(key)).setNWeapons(
						groups.get(key));
			}
			else {
				// need to add a new weapon
				String name = key.split(":")[0];
				int loc = Integer.parseInt(key.split(":")[1]);
				EquipmentType etype = EquipmentType.get(name);
				Mounted newmount;

				if (etype != null) {
					try {
						newmount = addWeaponGroup(etype, loc);
						newmount.setNWeapons(groups.get(key));
						weaponGroups.put(key, getEquipmentNum(newmount));
					}
					catch (LocationFullException ex) {
						System.out.println("Unable to compile weapon groups"); //$NON-NLS-1$
						ex.printStackTrace();
						return;
					}
				}
				else if (!"0".equals(name)) {
					addFailedEquipment(name);
				}
			}
		}
	}



	/**
	 * In cases where another unit occupies the same hex, determine if this Aero
	 * should be moved back a hex for targeting purposes.
	 * 
	 * @param other the Aero to check if it should be moved back.
	 * @return true if the Aero should be moved back.
	 */
	public boolean shouldMoveBackHex(Aero other)
	{
		if (null == getPosition()) {
			return false;
		}

		if (null == other.getPosition()) {
			return false;
		}

		if (!getPosition().equals(other.getPosition())) {
			return false;
		}

		int type = UnitType.determineUnitTypeCode(this);
		int otherType = UnitType.determineUnitTypeCode(other);
		int vel = getCurrentVelocity();
		int otherVel = other.getCurrentVelocity();

		if (type > otherType) {
			return false;
		}
		else if (type < otherType) {
			return true;
		}

		// if we are still here then type is the same so compare velocity
		if (vel < otherVel) {
			return false;
		}
		else if (vel > otherVel) {
			return true;
		}

		// if we are still here then type and velocity same, so roll for it
		if (getWhoFirst() < other.getWhoFirst()) {
			return false;
		}
		else {
			return true;
		}
	}



	/**
	 * Determines whether or not this Aero has an armored engine.
	 * 
	 * @see megamek.common.Entity#hasArmoredEngine()
	 */
	@Override
	public boolean hasArmoredEngine()
	{
		for (int slot = 0; slot < getNumberOfCriticals(LOC_AFT); slot++) {
			CriticalSlot cs = getCritical(LOC_AFT, slot);
			if ((cs != null) && (cs.getType() == CriticalSlot.TYPE_SYSTEM) &&
					(cs.getIndex() == Mech.SYSTEM_ENGINE)) {
				return cs.isArmored();
			}
		}

		return false;
	}
}